-
Notifications
You must be signed in to change notification settings - Fork 0
/
depcalc.hs
108 lines (98 loc) · 3.48 KB
/
depcalc.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
--
-- Simple inverted port dependency calculation
--
-- It tells you which ports are affected 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 qualified Data.Map as DM
import qualified Data.Graph as DG
import qualified Data.List as DL
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
. DL.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
buildNodes :: [(String,[String])] -> [(String,String,[String])]
buildNodes ports = map translation ports
where translation (n, ds) = (n, n, ds)
generateGraph :: Ports -> (PortGraph, DG.Vertex -> (String,String,[String]), String -> Maybe DG.Vertex)
generateGraph ports = DG.graphFromEdges $ buildNodes deps
where deps = getAllBuildDependencies ports
depends :: PortGraph -> String -> (DG.Vertex -> (String,String,[String])) -> (String -> Maybe DG.Vertex) -> [String]
depends graph port f g = DL.sort $ map translate $ DG.reachable graph v
where
v = case (g port) of
Just id -> id
Nothing -> error $ "Port \"" ++ port ++ "\" cannot be found (broken INDEX or mistyped name?)"
translate v = case (f v) of
(n,_,_) -> n
main = do
args <- getArgs
prog <- getProgName
if ((length args) < 2)
then putStrLn $ unlines
["Simple inverted port dependency calculation"
,""
,"It tells you which ports are affected by changing a given port. The result"
,"always includes the port itself."
,""
,"USAGE: " ++ prog ++ " DESCRIBE category/port"
,""
,""
,"DESCRIBE files can be constructed by the following command:"
,""
,"$ make -C ${PORTSDIR} describe | grep -v \"^===>\" > DESCRIBE"]
else do
let describe = args !! 0
let port = args !! 1
contents <- BS.readFile describe
let (g,f,v) = generateGraph $ ports contents
let graph = DG.transposeG g
let output = unlines $ depends graph port f v
putStrLn output