{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE TupleSections #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module GraphMod(plugin, collectImports) where
import GhcPlugins
import TcRnTypes
import HsExtension
import HsImpExp
import Binary
import Data.Maybe
import Data.List
import GraphMod.Utils as GraphMod
import GraphMod.Dot as GraphMod
import GraphMod.Args
import qualified GraphMod.Trie as Trie
import qualified Data.Map as Map
import System.FilePath
import System.Directory
import System.Console.GetOpt
import System.Environment(getArgs)
import System.IO
printStderr :: Show a => a -> IO ()
printStderr = hPutStrLn stderr . show
initBinMemSize :: Int
initBinMemSize = 1024 * 1024
plugin :: Plugin
plugin = defaultPlugin {
typeCheckResultAction = install
, pluginRecompile = impurePlugin
}
install :: [CommandLineOption] -> ModSummary -> TcGblEnv -> TcM TcGblEnv
install opts ms tc_gbl = do
let imps = tcg_rn_imports tc_gbl
gm_imps = map (convertImport . unLoc) imps
outdir = mkOutdir opts
path = mkPath outdir (ms_mod ms)
gm_modname = getModName ms
liftIO $ do
createDirectoryIfMissing False outdir
writeBinary path (gm_modname, gm_imps)
return tc_gbl
mkOutdir :: [CommandLineOption] -> FilePath
mkOutdir [] = defaultLocation
mkOutdir (x:_) = x
writeBinary :: Binary a => FilePath -> a -> IO ()
writeBinary path payload = do
bh <- openBinMem initBinMemSize
put_ bh payload
writeBinMem bh path
mkPath :: FilePath -> Module -> FilePath
mkPath fp m
= fp </> (moduleNameString (moduleName m) ++ (show (moduleUnitId m)))
type Payload = (GraphMod.ModName, [GraphMod.Import])
getModName :: ModSummary -> GraphMod.ModName
getModName ms = GraphMod.splitModName . moduleNameString . moduleName . ms_mod $ ms
convertImport :: ImportDecl GhcRn -> GraphMod.Import
convertImport (ImportDecl{..}) =
GraphMod.Import { impMod = convertModName (ideclName)
, impType = if ideclSource
then GraphMod.SourceImp
else GraphMod.NormalImp
}
convertImport _ = error "Unreachable"
convertModName :: Located ModuleName -> GraphMod.ModName
convertModName (L _ mn) = GraphMod.splitModName (moduleNameString mn)
readImports :: FilePath -> FilePath -> IO Payload
readImports outdir fp = do
readBinMem (outdir </> fp) >>= get
collectImports :: IO ()
collectImports = do
raw_opts <- getArgs
printStderr raw_opts
let (fs, _ms, _errs) = getOpt Permute options raw_opts
opts = foldr ($) default_opts fs
outdir = inputDir opts
printStderr $ ("OutDir: ", outdir)
files <- listDirectory outdir
printStderr $ ("files:", concat files)
usages <- mapM (readImports outdir) files
printStderr usages
let graph = buildGraph opts usages
putStr (GraphMod.make_dot opts graph)
modGraph :: [Payload] -> [GraphMod.ModName]
modGraph = nub . foldMap do_one
where
do_one (mn, is) = mn : map do_import is
do_import (GraphMod.Import n _) = n
buildGraph :: Opts -> [Payload] -> (GraphMod.AllEdges, GraphMod.Nodes)
buildGraph opts payloads = maybePrune opts (aes, process nodes)
where
process nodes = collapseAll opts nodes (collapse_quals opts)
nodeMapList = zip (modGraph payloads) [0..]
nodeMap = Map.fromList nodeMapList
nodes = foldr insertMod Trie.empty nodeMapList
aes = foldr (makeEdges nodeMap) GraphMod.noEdges
(concatMap (\(p, is) -> map (p,) is) payloads)
insertMod (n, k) t = GraphMod.insMod n k t
makeEdges :: Map.Map GraphMod.ModName Int
-> (GraphMod.ModName, GraphMod.Import)
-> GraphMod.AllEdges
-> GraphMod.AllEdges
makeEdges nodeMap (m_from, m_to) aes = fromMaybe (error "makeEdges") $ do
from_i <- Map.lookup m_from nodeMap
to_i <- Map.lookup (GraphMod.impMod m_to) nodeMap
return $ case GraphMod.impType m_to of
GraphMod.SourceImp ->
aes { GraphMod.sourceEdges
= GraphMod.insSet from_i to_i (GraphMod.sourceEdges aes) }
GraphMod.NormalImp ->
aes { GraphMod.normalEdges = GraphMod.insSet from_i to_i (GraphMod.normalEdges aes) }
instance Binary GraphMod.Import where
put_ bh (GraphMod.Import mn ip) = put_ bh mn >> put_ bh ip
get bh = GraphMod.Import <$> get bh <*> get bh
instance Binary GraphMod.ImpType where
put_ bh c =
case c of
GraphMod.NormalImp -> putByte bh 0
GraphMod.SourceImp -> putByte bh 1
get bh = getByte bh >>= return . \case
0 -> GraphMod.NormalImp
1 -> GraphMod.SourceImp
_ -> error "Binary:GraphMod"