-- -- Simple inverted port dependency calculation -- -- It tells you which ports are affected directly by changing a given port. -- The result always includes the port itself. -- -- Requires a "DESCRIBE" file which can be generated from the ports tree -- by issuing `make describe | grep -v "^===>" > DESCRIBE` in the root. -- import qualified Data.ByteString.Lazy as BS import qualified Data.ByteString.Lazy.Char8 as BSC8 import Data.List import Data.Maybe import qualified Data.Map as DM import qualified Data.Graph as DG import System.Environment type Name = BS.ByteString type BuildDeps = BS.ByteString type Port = (Name, BuildDeps) type Ports = [Port] type Deps = DM.Map String [String] type PortIds = DM.Map String Int type PortDis = DM.Map Int String type PortGraph = DG.Graph convertPath :: BS.ByteString -> BS.ByteString -> BS.ByteString convertPath category path = ( BSC8.concat . intersperse (BSC8.singleton '/') . (:) (norm cat) . take 1 . nsr ) path where normalize s = if ((BSC8.last s) == '/') then BSC8.init s else s norm cat = if (BSC8.unpack cat == "..") then category else cat nsr = reverse . BSC8.split '/' . normalize cat = (head . drop 1 . take 2 . nsr) path ports :: BS.ByteString -> Ports ports index = map (\line -> parse $ BSC8.split '|' line) $ BSC8.lines index where parse (package : path : prefix : comment : pkgdescr : maintainer : categories : _ : _ : _ : bdepends : rdepends : www : _) = (name, bdepends) where name = convertPath category path category = head $ BSC8.split ' ' categories getAllBuildDependencies :: Ports -> [(String,[String])] getAllBuildDependencies ports = map conversion ports where conversion (pname,pbds) = (name, deps) where name = BSC8.unpack pname deps = map (BSC8.unpack . convertPath category) $ BSC8.split ' ' pbds category = head $ BSC8.split '/' pname getAll :: Ports -> [String] getAll ports = map translation ports where translation (pname,_) = BSC8.unpack pname escapeName :: String -> String escapeName = BSC8.unpack . eliminateAll "/-.+" . BSC8.pack where eliminate c = BSC8.concat -- . intersperse (BSC8.pack "_") . BSC8.split c eliminateAll chars = foldr1 (.) $ map eliminate chars generateDot :: [(String,[String])] -> String generateDot ports = unlines $ [ "digraph G {" ] ++ labelPorts ++ listDeps ++ [ "}" ] where labelPorts = map (\p -> " " ++ (labelPort p)) ports labelPort (n,_) = (escapeName n) ++ " " ++ "[label = \"" ++ n ++ "\"]" listDeps = concatMap (\p -> listdeps p) ports listdeps (n,ds) = map (\d -> " " ++ (dep d)) ds where dep d = (escapeName n) ++ " -> " ++ (escapeName d) portPairs :: Ports -> [(String,Int)] portPairs ports = zip (map convert ports) [1..] where convert (p,_) = BSC8.unpack p portIds :: Ports -> PortIds portIds ports = DM.fromList $ portPairs ports portDis :: Ports -> PortDis portDis ports = DM.fromList $ map swap $ portPairs ports where swap ~(x,y) = (y,x) getId :: PortIds -> String -> Int getId map p = fromJust $ DM.lookup p map getDi :: PortDis -> Int -> String getDi map p = fromJust $ DM.lookup p map buildNodes :: [(String,[String])] -> PortIds -> [(String,Int,[Int])] buildNodes ports pids = map translation ports where translation (n,ds) = (n, id, mapds) where id = getId pids n mapds = map (getId pids) ds generateGraph :: Ports -> (PortGraph, DG.Vertex -> (String,Int,[Int]), Int -> Maybe DG.Vertex) generateGraph ports = DG.graphFromEdges $ buildNodes deps pids where deps = getAllBuildDependencies ports pids = portIds ports depends :: PortIds -> PortDis -> PortGraph -> String -> (DG.Vertex -> (String,Int,[Int])) -> (Int -> DG.Vertex) -> [String] depends pids pdis pgr port f g = sort $ map translate $ DG.reachable pgr pid where pid = g $ getId pids port translate v = case (f v) of (n,_,_) -> n main = do args <- getArgs prog <- getProgName if ((length args) < 2) then putStrLn $ "USAGE: " ++ prog ++ " DESCRIBE port" else do let describe = args !! 0 let port = args !! 1 contents <- BS.readFile describe let dsc = ports contents let pids = portIds dsc let pdis = portDis dsc let (g,f,v) = generateGraph dsc let pgrp = DG.transposeG g let output = unlines $ depends pids pdis pgrp port f (fromJust . v) putStrLn output