packages feed

hoogle-4.2.27: src/Recipe/All.hs

{-# LANGUAGE RecordWildCards, ScopedTypeVariables #-}

module Recipe.All(recipes) where

import General.Base hiding (readFile')
import General.System as Sys
import Control.Exception as E
import qualified Data.Set as Set
import Development.Shake
import Development.Shake.FilePath
import Recipe.Haddock

import Recipe.Warning
import Recipe.Command
import Recipe.Keyword
import Recipe.Hackage
import Recipe.Cabal
import Hoogle
import qualified Paths_hoogle as V
import Data.Version
import CmdLine.All


-- CmdLine is guaranteed to be a constructor of type Data
recipes :: CmdLine -> IO ()
recipes opt@Data{..} = withModeGlobalRead $ do
    hSetBuffering stdout NoBuffering
    createDirectoryIfMissing True datadir
    withDirectory datadir $ do
        resetWarnings
        when redownload $ do
            forM_ urls $ \(file,_) -> removeFile_ $ "downloads" </> file
        shake shakeOptions{shakeVersion=showVersion V.version, shakeThreads=threads, shakeProgress=progressSimple} $ do
            want $ map (<.> "hoo") $ if null actions then ["default"] else actions
            rules opt
        recapWarnings
        putStrLn "Data generation complete"


rules :: CmdLine -> Rules ()
rules Data{..} = do
    "downloads/packages.txt" *> \out -> do
        need ["downloads/hoogle.untar","downloads/cabal.untar"]
        as <- liftIO $ listing "downloads/hoogle"
        bs <- liftIO $ listing "downloads/cabal"
        writeFileLines out $ Set.toList $ Set.fromList as `Set.intersection` Set.fromList bs

    packages <- do
        cache <- newCache $ fmap (Set.fromList . lines) . readFile
        return $ cache "downloads/packages.txt"

    phony "all.hoo" $ do
        pkgs <- packages
        need $ map (<.> "hoo") $ "default" : Set.toList pkgs

    "keyword.txt" *> \out -> do
        let src = "downloads/keyword.htm"
        need [src]
        contents <- liftIO $ readFileUtf8' src
        liftIO $ writeFileUtf8 out $ translateKeywords contents

    "default.txt" *> \out -> do
        writeFileLines out ["@combine keyword","@combine package","@combine platform"]

    "platform.txt" *> \out -> do
        let src = "downloads/platform.cabal"
        contents <- readFile' src
        writeFileLines out ["@combine " ++ x | x <- platformPackages contents]

    "package.txt" *> \out -> do
        need ["downloads/cabal.untar"]
        xs <- liftIO $ listing "downloads/cabal"
        xs <- liftIO $ forM xs $ \name -> do
            ver <- version "downloads/cabal" name
            let file = "downloads/cabal" </> name </> ver </> name <.> "cabal"
            src <- readCabal file
            return $ case src of
                Nothing -> []
                Just src ->
                    [""] ++ zipWith (++) ("-- | " : repeat "--   ") (cabalDescription src) ++
                    ["--","-- Version " ++ ver, "@url package/" ++ name, "@entry package " ++ name]
        liftIO $ writeFileUtf8 out $ unlines $ "@url http://hackage.haskell.org/" : "@package package" : concat xs

    (\x -> "*.txt" ?== x && takeBaseName x `notElem` ["keyword","default","platform","package"]) ?> \out -> do
        need ["downloads/hoogle.untar","downloads/cabal.untar"]
        let name = takeBaseName out
            base = name == "base"
        vc <- liftIO $ version "downloads/cabal" name
        vh <- liftIO $ if base then return vc else version "downloads/hoogle" name
        let hoo = if base then "downloads/base.txt" else "downloads/hoogle" </> name </> vh </> "doc" </> "html" </> name <.> "txt"
            cab = "downloads/cabal" </> name </> vc </> name <.> "cabal"
        need [hoo, cab]
        hoo <- liftIO $ readFileUtf8' hoo `E.catch` \(_ :: SomeException) -> readFile hoo
        deps <- liftIO $ fmap (maybe [] cabalDepends) $ readCabal cab
        let cleanDeps = deps \\ (name:avoid)
        loc <- liftIO $ findLocal local name
        liftIO $ writeFileUtf8 out $ unlines $
            ["@depends " ++ a | a <- cleanDeps] ++ haddockHacks loc (lines hoo)

    imported <- newCache $ \file -> do
        xs <- readFileUtf8' file
        return [x | x <- lines xs, takeWhile (not . isSpace) x `elem` ["type","data","newtype","class","instance","@depends"]]
    let listDeps = map (drop 9) . takeWhile ("@depends " `isPrefixOf`)

    let genImported pkgs seen [] = return []
        genImported pkgs seen (t:odo)
            | t `Set.member` seen || not (t `Set.member` pkgs) = genImported pkgs seen odo
            | otherwise = do
                i <- imported $ t <.> "txt"
                let deps = listDeps i
                fmap (i++) $ genImported pkgs (Set.insert t seen) (deps++odo)

    (\x -> "*.hoo" ?== x && x /= "all.hoo") ?> \out -> do
        let src = out -<.> "txt"
        need [src]
        contents <- liftIO $ fmap lines $ readFileUtf8' src
        if not (null contents) && "@combine " `isPrefixOf` head contents then do
            let deps = [x <.> "hoo" | x <- contents, Just x <- [stripPrefix "@combine " x]]
            need deps
            dbs <- liftIO $ mapM loadDatabase deps
            putNormal $ "Creating " ++ out ++ " from " ++ show (length deps) ++ " databases... "
            liftIO $ performGC
            liftIO $ saveDatabase out $ mconcat dbs
         else do
            pkgs <- packages
            deps <- genImported pkgs (Set.singleton $ takeBaseName out) $ listDeps contents
            let (err,db) = createDatabase Haskell [snd $ createDatabase Haskell [] $ unlines deps] $ unlines contents
            unless (null err) $ putNormal $ "Skipped " ++ show (length err) ++ " warnings in " ++ out
            putLoud $ unlines $ map show err
            putNormal $ "Creating " ++ out ++ "... "
            liftIO $ performGC
            liftIO $ saveDatabase out db

    "//*.tar" *> \out -> do
        let src = out <.> "gz"
        need [src]
        ungzip src out

    "//*.untar" *> \out -> do
        let src = out -<.> "tar"
        need [src]
        untar src
        writeFile' out ""

    (\x -> "downloads/*" ?== x && isJust (lookup (takeFileName x) urls)) ?> \out -> do
        let Just url = lookup (takeFileName out) urls
        -- liftIO $ copyFile ("C:/spacework/hoogle/cache" </> takeFileName out) out
        wget url out


urls :: [(FilePath, URL)]
urls = let (*) = (,) in
    ["keyword.htm" * "http://www.haskell.org/haskellwiki/Keywords"
    ,"platform.cabal" * "http://code.galois.com/darcs/haskell-platform/haskell-platform.cabal"
    ,"base.txt" * "http://www.haskell.org/hoogle/base.txt"
    ,"ghc.txt" * "http://www.haskell.org/ghc/docs/latest/html/libraries/ghc/ghc.txt"
    ,"cabal.tar.gz" * "http://hackage.haskell.org/packages/index.tar.gz"
    ,"hoogle.tar.gz" * "http://hackage.haskell.org/packages/hoogle.tar.gz"]


listing :: FilePath -> IO [String]
listing dir = do
    xs <- Sys.getDirectoryContents dir
    return $ sortBy (comparing $ map toLower) $ filter (`notElem` [".","..","preferred-versions"]) xs

version :: FilePath -> String -> IO String
version dir x = do
    ys <- Sys.getDirectoryContents $ dir </> x
    when (null ys) $ error $ "Couldn't find version for " ++ x ++ " in " ++ dir
    let f = map (read :: String -> Int) . words . map (\x -> if x == '.' then ' ' else x)
    return $ maximumBy (comparing f) $ filter (all (not . isAlpha)) ys