...
1
2
3
4
5
6
7 package importgraph
8
9 import (
10 "go/build"
11 "sync"
12
13 "golang.org/x/tools/go/buildutil"
14 )
15
16
17
18
19
20
21
22
23
24
25 type Graph map[string]map[string]bool
26
27 func (g Graph) addEdge(from, to string) {
28 edges := g[from]
29 if edges == nil {
30 edges = make(map[string]bool)
31 g[from] = edges
32 }
33 edges[to] = true
34 }
35
36
37
38
39 func (g Graph) Search(roots ...string) map[string]bool {
40 seen := make(map[string]bool)
41 var visit func(x string)
42 visit = func(x string) {
43 if !seen[x] {
44 seen[x] = true
45 for y := range g[x] {
46 visit(y)
47 }
48 }
49 }
50 for _, root := range roots {
51 visit(root)
52 }
53 return seen
54 }
55
56
57
58
59
60
61
62 func Build(ctxt *build.Context) (forward, reverse Graph, errors map[string]error) {
63 type importEdge struct {
64 from, to string
65 }
66 type pathError struct {
67 path string
68 err error
69 }
70
71 ch := make(chan interface{})
72
73 go func() {
74 sema := make(chan int, 20)
75 var wg sync.WaitGroup
76 buildutil.ForEachPackage(ctxt, func(path string, err error) {
77 if err != nil {
78 ch <- pathError{path, err}
79 return
80 }
81
82 wg.Add(1)
83 go func() {
84 defer wg.Done()
85
86 sema <- 1
87 bp, err := ctxt.Import(path, "", 0)
88 <-sema
89
90 if err != nil {
91 if _, ok := err.(*build.NoGoError); ok {
92
93 } else {
94 ch <- pathError{path, err}
95 }
96
97 }
98
99
100
101
102
103
104
105
106
107
108
109
110 memo := make(map[string]string)
111 absolutize := func(path string) string {
112 canon, ok := memo[path]
113 if !ok {
114 sema <- 1
115 bp2, _ := ctxt.Import(path, bp.Dir, build.FindOnly)
116 <-sema
117
118 if bp2 != nil {
119 canon = bp2.ImportPath
120 } else {
121 canon = path
122 }
123 memo[path] = canon
124 }
125 return canon
126 }
127
128 if bp != nil {
129 for _, imp := range bp.Imports {
130 ch <- importEdge{path, absolutize(imp)}
131 }
132 for _, imp := range bp.TestImports {
133 ch <- importEdge{path, absolutize(imp)}
134 }
135 for _, imp := range bp.XTestImports {
136 ch <- importEdge{path, absolutize(imp)}
137 }
138 }
139
140 }()
141 })
142 wg.Wait()
143 close(ch)
144 }()
145
146 forward = make(Graph)
147 reverse = make(Graph)
148
149 for e := range ch {
150 switch e := e.(type) {
151 case pathError:
152 if errors == nil {
153 errors = make(map[string]error)
154 }
155 errors[e.path] = e.err
156
157 case importEdge:
158 if e.to == "C" {
159 continue
160 }
161 forward.addEdge(e.from, e.to)
162 reverse.addEdge(e.to, e.from)
163 }
164 }
165
166 return forward, reverse, errors
167 }
168
View as plain text