gpah-0.0.2: src/Generics/GPAH/Main.hs
module Main where
import Generics.GPAH.Base
import Generics.GPAH.Conf
import Generics.GPAH.Utils
-- the sub-components
import qualified Generics.GPAH.Cpp
import qualified Generics.GPAH.Deriving
import qualified Generics.GPAH.Function
import qualified Generics.GPAH.Date
import qualified Generics.GPAH.Hackage
import qualified Generics.GPAH.Upl
import qualified Generics.GPAH.Interp
import qualified Generics.GPAH.Derive
import Control.Exception (catch, SomeException (..), evaluate)
import Control.DeepSeq (force)
import Control.Monad
import Data.Monoid
import System.IO
-- Cabal-related imports
import Distribution.PackageDescription
import Distribution.PackageDescription.Parse (readPackageDescription)
import Distribution.Verbosity (silent)
import Distribution.Package
import System.FilePath
import System.Directory (removeDirectoryRecursive)
-- for doing the python job
import qualified Codec.Archive.Tar as Tar
import qualified Codec.Compression.GZip as GZip
import qualified Data.ByteString.Lazy.Char8 as BS
main = tryFetch >> analyze >>= pprint
tryFetch :: IO ()
tryFetch = when (fetchOpt conf) $ do
log <- downloadURL "http://old.hackage.haskell.org/packages/archive/log"
case log of
Right res -> do
-- Write the hackage log
file <- openBinaryFile (hackageLogOpt conf) WriteMode
hPutStr file res
hClose file
Left x -> error x
tar <- downloadURL "http://old.hackage.haskell.org/packages/archive/00-archive.tar"
case tar of
Right res -> do
-- Unpack the hackage archive tar
Tar.unpack (hackageDirOpt conf) $ Tar.read $ BS.pack res
-- Unpack each package in the hackage dir
pkgNames <- getHackagePkgsNames
mapM_ (\ pkgName -> do
pkgVersion <- getPkgVersion pkgName
let fp = (hackageDirOpt conf </> pkgName </> pkgVersion)
catch (do
h <- openBinaryFile (fp </> (pkgName ++ "-" ++ pkgVersion) <.> "tar.gz") ReadMode
c <- BS.hGetContents h
catch (Tar.unpack fp (Tar.read ( GZip.decompress c)) >> hClose h)
(\ (SomeException _) -> do
hClose h
removeDirectoryRecursive (hackageDirOpt conf </> pkgName)
)
)
(\ (SomeException _) ->
removeDirectoryRecursive (hackageDirOpt conf </> pkgName)
)
) pkgNames
Left x -> error x
print "Done Fetching & Unpacking"
analyze :: IO Analysis
analyze = if hasSubComponent conf -- check for enabled subcomponents, so not to pointlessly traverse the modules
then (print "Running the analysis" >> getHackagePkgsNames >>= mapM analyzePkg >>= return . mconcat >>= appendAnalyzeDate)
else return mempty
appendAnalyzeDate :: Analysis -> IO Analysis
appendAnalyzeDate a = maybe
(return a) -- not enabled in the conf, skip
(const $ do
contents <- readFile (hackageLogOpt conf)
return $ Generics.GPAH.Date.analyzeHackageLog contents a `mappend` a -- appends it to the main analysis
)
(dateOpt conf)
analyzePkg :: String -> IO Analysis
analyzePkg pkgName = do
vsn <- getPkgVersion pkgName
let cabal = pkgCabal pkgName vsn :: FilePath -- absolute cabal files
parsedCabal <- readPackageDescription silent cabal :: IO GenericPackageDescription
let pkgAbsDir = pkgDir pkgName vsn -- the absolute directories of package
let pkgSrcDirs = getSrcDirs parsedCabal :: [FilePath] -- fetch the source directories , relevant to package
let pkgAbsSrcDirs = map (pkgAbsDir </>) pkgSrcDirs :: [FilePath] -- absolute source directories
haskellSrcs <- return . concat . zipWith (\ abssrcdir srcs -> zip (repeat abssrcdir) srcs) pkgAbsSrcDirs =<< mapM getHaskellSrcs pkgAbsSrcDirs
let hacPkg = maybe mempty (const $ Generics.GPAH.Hackage.analyzePackage pkgName parsedCabal) (hackageOpt conf `mplus` dateOpt conf `mplus` intOpt conf `mplus` dveOpt conf) -- -t or -i or -e, imply -h
let analyzeModule (pkgAbsSrcDir, hs) = do
cpp <- maybe (return mempty) (const $ Generics.GPAH.Cpp.analyzeModule hs pkgAbsDir parsedCabal) (cppOpt conf)
int <- maybe (return mempty) (const $ Generics.GPAH.Interp.analyzeModule hs pkgName pkgAbsSrcDir hacPkg) (intOpt conf)
parseRes <- parseModuleFile hs
-- turn on specific sub analyses based on user-provided conf
let der = maybe mempty (const $ Generics.GPAH.Deriving.analyzeModule parseRes) (derivingOpt conf)
fun = maybe mempty (const $ Generics.GPAH.Function.analyzeModule parseRes parsedCabal) (functionOpt conf)
upl = maybe mempty (const $ Generics.GPAH.Upl.analyzeModule parseRes parsedCabal) (uniplateOpt conf)
hac = maybe mempty (const $ Generics.GPAH.Hackage.analyzeModule parseRes) (hackageOpt conf)
dve = maybe mempty (const $ Generics.GPAH.Derive.analyzeModule parseRes hacPkg) (dveOpt conf)
evaluate . force $ Analysis cpp der fun upl hac mempty int dve
let appendAnalyzeHacPkg a = a { hacAnalysis = hacAnalysis a `mappend` hacPkg }
return . appendAnalyzeHacPkg . mconcat =<< mapM analyzeModule haskellSrcs
pprint :: Analysis -> IO ()
pprint (Analysis cpp der fun upl hac dte int dve) = do
maybe (return ()) (Generics.GPAH.Cpp.pprint cpp) (cppOpt conf)
maybe (return ()) (Generics.GPAH.Hackage.pprint hac) (hackageOpt conf)
maybe (return ()) (Generics.GPAH.Deriving.pprint der) (derivingOpt conf)
maybe (return ()) (Generics.GPAH.Function.pprint fun) (functionOpt conf)
maybe (return ()) (Generics.GPAH.Upl.pprint upl) (uniplateOpt conf)
maybe (return ()) (Generics.GPAH.Date.pprint dte) (dateOpt conf)
maybe (return ()) (Generics.GPAH.Interp.pprint int) (intOpt conf)
maybe (return ()) (Generics.GPAH.Derive.pprint dve) (dveOpt conf)