packages feed

cabal-src-0.2.1: cabal-src-install.hs

import System.Process (runProcess, waitForProcess)
import System.Environment (getArgs)
import System.Exit (ExitCode (ExitSuccess), exitWith)
import Control.Monad (unless, when, forM_)
import System.Directory
import Data.List (isSuffixOf, isPrefixOf)
import qualified Codec.Archive.Tar as Tar
import qualified Codec.Archive.Tar.Entry as TE
import Data.Monoid (mempty)
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
import Control.Applicative ((<$>))
import Control.Exception (throw)
import System.FilePath ((</>))

rawSystem' :: String -> [String] -> FilePath -> IO ()
rawSystem' a b wdir = do
    ph <- runProcess a b (Just wdir) Nothing Nothing Nothing Nothing
    ec <- waitForProcess ph
    unless (ec == ExitSuccess) $ exitWith ec

main :: IO ()
main = do
    args <- getArgs
    let isSrcOnly = args == ["--src-only"]
    unless isSrcOnly $ rawSystem' "cabal" ("install" : args) "."
    hasSources <- doesFileExist "sources.txt"
    if hasSources
        then do
            ls <- fmap lines $ readFile "sources.txt"
            forM_ ls $ \l -> do
                exists <- doesDirectoryExist l
                when exists $ do
                    files <- getDirectoryContents l
                    when (any (".cabal" `isSuffixOf`) files) $ installSrc l
        else installSrc "."

installSrc :: FilePath -> IO ()
installSrc root = do
    putStrLn $ "Installing source package: " ++ root
    let dist = root </> "dist"
    distExists <- doesDirectoryExist dist
    when distExists $
        getDirectoryContents dist >>= mapM_ (\fp ->
            when (".tar.gz" `isSuffixOf` fp) $ removeFile $ dist </> fp)
    rawSystem' "cabal" ["sdist"] root
    files <- getDirectoryContents dist
    case filter (".tar.gz" `isSuffixOf`) files of
        [x] -> do
            let y = drop 1 $ dropWhile (/= '.')
                  $ drop 1 $ dropWhile (/= '.')
                  $ reverse x
            let (ver', name') = break (== '-') y
            let ver = reverse ver'
            let name = reverse $ drop 1 name'
            addToDB root dist name ver
        [] -> error "Missing tarball"
        _ -> error "Too many tarballs"

addToDB root dist name ver = do
    cabal <- getAppUserDataDirectory "cabal"
    let pd = cabal ++ "/packages/cabal-src/"
    createDirectoryIfMissing True pd
    let tb = pd ++ "00-index.tar"
    e <- doesFileExist tb
    entries <-
        if e
            then Tar.foldEntries (:) [] throw . Tar.read . L.fromChunks . return
                <$> S.readFile tb
            else return []
    cabalLBS <- L.readFile $ root </> name ++ ".cabal"
    Right tarPath <- return $ TE.toTarPath False $ concat
        [name, "/", ver, "/", name, "-", ver, ".cabal"]
    let entry = TE.fileEntry tarPath cabalLBS
    let entries' = entry : filter (\e -> TE.entryTarPath e /= tarPath) entries
    L.writeFile tb $ Tar.write entries'

    let dir = pd ++ concat [name, "/", ver, "/"]
    createDirectoryIfMissing True dir
    let filename = concat [name, "-", ver, ".tar.gz"]
    copyFile (dist </> filename) (dir ++ filename)
    fixConfig pd $ cabal ++ "/config"

fixConfig pd fn = do
    ls' <- lines <$> readFile fn
    let oldLines =
            [ "remote-repo: cabal-src:http://www.haskell.org/"
            ]
    let s = "local-repo: " ++ pd
    let ls = filter (not . flip elem oldLines) ls'
    unless (s `elem` ls) $ writeFile fn $ unlines $ addRepo s ls

addRepo s [] = [s]
addRepo s (x:xs)
    | "remote-repo:" `isPrefixOf` x = s : x : xs
    | otherwise = x : addRepo s xs