packages feed

gpah-0.0.1: src/Generics/GPAH/Utils.hs

module Generics.GPAH.Utils  where
    
import Generics.GPAH.Conf

import System.FilePath
import System.Directory
import Data.List
import Control.Monad (filterM, liftM)
import Data.Char (isSpace)
import Language.Haskell.Exts
import Language.Haskell.Exts.Comments
import Network.HTTP
import Network.URI
import Data.Maybe (fromJust)

import System.IO
import qualified Data.ByteString.Char8 as B

-- Cabal-related imports
import Distribution.PackageDescription
import Distribution.PackageDescription.Parse (readPackageDescription)
import Distribution.Verbosity (silent)
import Distribution.Package
import qualified Distribution.Compiler as Compiler (CompilerFlavor (GHC))

type ClassName = String
type DataName = String
type LineNumber = Int
type ModuleFileName = String

fromBool :: Bool -> Int
fromBool True = 1
fromBool False = 0

toBool :: Int -> Bool
toBool 1 = True
toBool 0 = False

-- returns the package directory relative to the hackage archive
-- e.g. hackage/syb/0.3.6/ACTUAL_PKG_DIR
pkgDir  :: String -> String -> FilePath
pkgDir pkg vsn = hackageDirOpt conf </> pkg </>  vsn </> pkg ++ "-" ++ vsn

pkgCabal :: FilePath -> FilePath -> FilePath
pkgCabal pkg vsn = pkgDir pkg vsn </> pkg ++ ".cabal"

-- ignore package hsc3, because of error:
-- gpanalysis.exe: In file hackage\hsc3\0.14\hsc3-0.14\Help\UGen\Panner\splay.help.lhs at line 1: program line before comment line.
-- 
getHackagePkgsNames :: IO [String]
getHackagePkgsNames = liftM (delete "hsc3") $ getSubDirs (hackageDirOpt conf)


getPkgVersion :: String -> IO String
getPkgVersion pkgName = liftM head $ getSubDirs (hackageDirOpt conf </> pkgName)

getSubDirs fp = do 
  ls <- getDirectoryContents fp 
  let clean_ls = ls \\ [".",".."]
  subdirs <- filterM (doesDirectoryExist . (fp </>)) clean_ls
  return subdirs


getSrcDirs :: GenericPackageDescription -> [FilePath]
getSrcDirs (GenericPackageDescription {condLibrary = l, condExecutables = es, condTestSuites = ts}) = 
  let getSrcDirsLib = hsSourceDirs . libBuildInfo 
      getSrcDirsExec  = hsSourceDirs . buildInfo
      getSrcDirsTest = hsSourceDirs . testBuildInfo
      pkgSrcDirs = nub $ concat $ 
                   maybe [[]] ((: []) . getSrcDirsLib . condTreeData) l ++ 
                   map (getSrcDirsExec . condTreeData . snd) es ++ 
                   map (getSrcDirsTest . condTreeData . snd) ts
  in if null pkgSrcDirs 
     then [""] -- the srcdir of the package is its home dir
     else pkgSrcDirs

-- if using Data.Text, this function is provided
-- and is more efficient
strip      :: String -> String
strip      = f . f
    where f = reverse . dropWhile isSpace


ghcOpts :: GenericPackageDescription -> String
ghcOpts (GenericPackageDescription {condLibrary = l, condExecutables = es, condTestSuites = ts}) =
    unwords [libGhcOptions,
             exesGhcOptions,
             testsGhcOptions]
    where
      libGhcOptions = maybe "" (ghcOptionsBI . libBuildInfo . condTreeData)  l
      exesGhcOptions = unwords $ map (ghcOptionsBI . buildInfo .  condTreeData . snd) es
      testsGhcOptions = unwords $ map (ghcOptionsBI . testBuildInfo .  condTreeData . snd) ts
      ghcOptionsBI :: BuildInfo -> String
      ghcOptionsBI (BuildInfo {options = opts}) = maybe "" unwords $ lookup Compiler.GHC opts


      
cppOpts :: GenericPackageDescription -> String
cppOpts (GenericPackageDescription {condLibrary = l, condExecutables = es, condTestSuites = ts}) =
    (strip . unwords) [libCppOptions,
                       exesCppOptions,
                       testsCppOptions,
                       libIncludeDirs,
                       exesIncludeDirs,
                       testsIncludeDirs]
    where
      libCppOptions = maybe "" (cppOptionsBI . libBuildInfo . condTreeData)  l
      exesCppOptions = unwords $ map (cppOptionsBI . buildInfo .  condTreeData . snd) es
      testsCppOptions = unwords $ map (cppOptionsBI . testBuildInfo .  condTreeData . snd) ts
      cppOptionsBI :: BuildInfo -> String
      cppOptionsBI (BuildInfo {cppOptions = opts}) = unwords opts

      libIncludeDirs = maybe "" (includeDirsBI . libBuildInfo . condTreeData)  l
      exesIncludeDirs = unwords $ map (includeDirsBI . buildInfo .  condTreeData . snd) es
      testsIncludeDirs = unwords $ map (includeDirsBI . testBuildInfo .  condTreeData . snd) ts
      includeDirsBI :: BuildInfo -> String
      includeDirsBI (BuildInfo {includeDirs = opts}) = unwords $ map ("-I" ++) opts
      


parseCabalSrcDirs :: FilePath -> IO [String]
parseCabalSrcDirs fp = do
  gpkgdesc <- readPackageDescription silent fp
  return (getSrcDirs gpkgdesc)


getHaskellSrcs :: FilePath -> IO [FilePath]
getHaskellSrcs fp = do
  existsDir <- doesDirectoryExist fp -- it's possible that the srcdir is not there
  if existsDir 
    then do  
    ls <- getDirectoryContents fp 
    -- filter hs, lhs and not Setup.hs and Setup.lhs and no hidden
    let hs_lhs = filter (\ l -> (".hs" `isSuffixOf` l || ".lhs" `isSuffixOf` l) && not ("." `isPrefixOf` l) && (l /= "Setup.hs") && (l /= "Setup.lhs")) ls

    -- It prefers the ".hspp" version of the source file, over the ".hs" or ".lhs" version of it
    hs_lhs_hspp <- mapM (\ hsOrlhsFile -> do 
                          let hsppFile = addExtension (dropExtension hsOrlhsFile) ".hspp"
                          p <- doesFileExist (fp </> hsppFile)
                          return $ if p then hsppFile else hsOrlhsFile)
                       hs_lhs

    subDirs <- getSubDirs fp

    if null subDirs 
      then do
      return $ map (fp </>) hs_lhs_hspp
      else do
      hs_lhs_hspp' <- mapM getHaskellSrcs (map (fp </>) subDirs)
      return $ map (fp </>) hs_lhs_hspp ++ (concat hs_lhs_hspp')
    else do 
    return []


parseModuleFile :: FilePath -> IO (ParseResult (Module, [Comment]))
parseModuleFile fp = do

  h <- openFile fp ReadMode

  te <- mkTextEncoding "UTF-8//IGNORE"
  hSetEncoding h te

  contents <-  return . B.unpack =<< B.hGetContents h

  hClose h

  -- remove pragmas
  let clearedContents = removePragmas contents

  -- set parsing mode
  let parseMode = defaultParseMode { parseFilename = fp
                                   , extensions = knownExtensions -- support extensions
                                   , fixities = Nothing }         -- no infix operators
  return $ parseFileContentsWithComments parseMode clearedContents


removePragmas :: String -> String
removePragmas s = unlines $ map removePragma (lines s) where
  removePragma ('#':xs) = ""
  removePragma xs = xs


downloadURL :: String -> IO (Either String String)
downloadURL url =
    do resp <- simpleHTTP request
       case resp of
         Left x -> return $ Left ("Error connecting: " ++ show x)
         Right r -> 
             case rspCode r of
               (2,_,_) -> return $ Right (rspBody r)
               (3,_,_) -> -- A HTTP redirect
                 case findHeader HdrLocation r of
                   Nothing -> return $ Left (show r)
                   Just url -> downloadURL url
               _ -> return $ Left (show r)
    where request = Request {rqURI = uri,
                             rqMethod = GET,
                             rqHeaders = [],
                             rqBody = ""}
          uri = fromJust $ parseURI url


-- Libraries under the "Generics" , "Generic" categories of HackageDB + "syb-with-class" + "derive" + "DrIFT-cabalized"
libsGP = ["HsTools",
          "syb-extras",
          "algebraic-classes",
          "alloy",
          "alloy-proxy-fd",
          "Annotations",
          "compdata",
          "derive-IG",
          "emgm",
          "fixplate",
          "functorm",
          "gdiff-ig",
          "gdiff-th",
          "generic-binary",
          "generic-deepseq",
          "generic-deriving",
          "GenericPretty",
          "geniplate",
          "guarded-rewriting",
          "instant-generics",
          "instant-zipper",
          "ligd",
          "ListLike",
          "listlike-instances",
          "multifocal",
          "multiplate",
          "multiplate-simplified",
          "multirec",
          "multirec-alt-deriver",
          "multirec-binary",
          "one-liner",
          "pointless-haskell",
          "pointless-lenses",
          "pointless-rewrite",
          "putlenses",
          "regular",
          "regular-extras",
          "regular-web",
          "RepLib",
          "rewriting",
          "special-functors",
          "spine",
          "Strafunski-StrategyLib",
          "StrategyLib",
          "syb",
          "syz",
          "TYB",
          "unbound",
          "unfoldable",
          "uniplate",
          "xformat",
          "yoko",
          "zipper",
          "syb-with-class",
          "derive",
          "DrIFT-cabalized"
         ]