GoPLS Viewer

Home|gopls/refactor/importgraph/graph.go
1// Copyright 2014 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package importgraph computes the forward and reverse import
6// dependency graphs for all packages in a Go workspace.
7package importgraph // import "golang.org/x/tools/refactor/importgraph"
8
9import (
10    "go/build"
11    "sync"
12
13    "golang.org/x/tools/go/buildutil"
14)
15
16// A Graph is an import dependency graph, either forward or reverse.
17//
18// The graph maps each node (a package import path) to the set of its
19// successors in the graph.  For a forward graph, this is the set of
20// imported packages (prerequisites); for a reverse graph, it is the set
21// of importing packages (clients).
22//
23// Graph construction inspects all imports in each package's directory,
24// including those in _test.go files, so the resulting graph may be cyclic.
25type Graph map[string]map[string]bool
26
27func (g GraphaddEdge(fromto 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// Search returns all the nodes of the graph reachable from
37// any of the specified roots, by following edges forwards.
38// Relationally, this is the reflexive transitive closure.
39func (g GraphSearch(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// Build scans the specified Go workspace and builds the forward and
57// reverse import dependency graphs for all its packages.
58// It also returns a mapping from canonical import paths to errors for packages
59// whose loading was not entirely successful.
60// A package may appear in the graph and in the errors mapping.
61// All package paths are canonical and may contain "/vendor/".
62func Build(ctxt *build.Context) (forwardreverse Grapherrors map[string]error) {
63    type importEdge struct {
64        fromto 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 int20// I/O concurrency limiting semaphore
75        var wg sync.WaitGroup
76        buildutil.ForEachPackage(ctxt, func(path stringerr error) {
77            if err != nil {
78                ch <- pathError{patherr}
79                return
80            }
81
82            wg.Add(1)
83            go func() {
84                defer wg.Done()
85
86                sema <- 1
87                bperr := ctxt.Import(path""0)
88                <-sema
89
90                if err != nil {
91                    if _ok := err.(*build.NoGoError); ok {
92                        // empty directory is not an error
93                    } else {
94                        ch <- pathError{patherr}
95                    }
96                    // Even in error cases, Import usually returns a package.
97                }
98
99                // absolutize resolves an import path relative
100                // to the current package bp.
101                // The absolute form may contain "vendor".
102                //
103                // The vendoring feature slows down Build by 3×.
104                // Here are timings from a 1400 package workspace:
105                //    1100ms: current code (with vendor check)
106                //     880ms: with a nonblocking cache around ctxt.IsDir
107                //     840ms: nonblocking cache with duplicate suppression
108                //     340ms: original code (no vendor check)
109                // TODO(adonovan): optimize, somehow.
110                memo := make(map[string]string)
111                absolutize := func(path stringstring {
112                    canonok := memo[path]
113                    if !ok {
114                        sema <- 1
115                        bp2_ := ctxt.Import(pathbp.Dirbuild.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{pathabsolutize(imp)}
131                    }
132                    for _imp := range bp.TestImports {
133                        ch <- importEdge{pathabsolutize(imp)}
134                    }
135                    for _imp := range bp.XTestImports {
136                        ch <- importEdge{pathabsolutize(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 // "C" is fake
160            }
161            forward.addEdge(e.frome.to)
162            reverse.addEdge(e.toe.from)
163        }
164    }
165
166    return forwardreverseerrors
167}
168
MembersX
Graph.addEdge.to
Graph.Search
Build.ch
buildutil
Graph.Search.roots
Build.BlockStmt.wg
Build.BlockStmt.BlockStmt.BlockStmt.BlockStmt.BlockStmt._
Build.BlockStmt.BlockStmt.BlockStmt.BlockStmt.BlockStmt.bp2
build
Graph
Graph.Search.g
Graph.Search.RangeStmt_1489.root
Build.reverse
Build.pathError
Build.BlockStmt.BlockStmt.BlockStmt.BlockStmt.RangeStmt_3819.imp
Graph.addEdge.g
Graph.Search.seen
Graph.Search.BlockStmt.BlockStmt.RangeStmt_1441.y
Build.importEdge.from
Build.importEdge.to
sync
Graph.addEdge.from
Build.importEdge
Build.pathError.err
Build.BlockStmt.BlockStmt.BlockStmt.bp
Build
Build.BlockStmt.sema
Build.BlockStmt.BlockStmt.BlockStmt.BlockStmt.RangeStmt_3724.imp
Graph.addEdge
Build.ctxt
Build.errors
Build.pathError.path
Build.RangeStmt_4007.e
Build.forward
Build.BlockStmt.BlockStmt.BlockStmt.err
Build.BlockStmt.BlockStmt.BlockStmt.memo
Build.BlockStmt.BlockStmt.BlockStmt.BlockStmt.RangeStmt_3633.imp
Members
X