-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgraphviz.go
106 lines (92 loc) · 2.83 KB
/
graphviz.go
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
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nin
import (
"fmt"
"io"
"os"
"strings"
)
// GraphViz is the object to initialize the parameters to create GraphViz .dot
// file output.
type GraphViz struct {
out io.Writer
dyndepLoader DyndepLoader
visitedNodes map[*Node]struct{}
visitedEdges map[*Edge]struct{}
}
// NewGraphViz returns an initialized GraphViz.
func NewGraphViz(state *State, di DiskInterface) GraphViz {
return GraphViz{
out: os.Stdout,
dyndepLoader: NewDyndepLoader(state, di),
visitedNodes: map[*Node]struct{}{},
visitedEdges: map[*Edge]struct{}{},
}
}
// AddTarget adds a node to include in the graph.
func (g *GraphViz) AddTarget(node *Node) {
if _, ok := g.visitedNodes[node]; ok {
return
}
fmt.Fprintf(g.out, "\"%p\" [label=\"%s\"]\n", node, strings.ReplaceAll(node.Path, "\\", "/"))
g.visitedNodes[node] = struct{}{}
edge := node.InEdge
if edge == nil {
// Leaf node.
// Draw as a rect?
return
}
if _, ok := g.visitedEdges[edge]; ok {
return
}
g.visitedEdges[edge] = struct{}{}
if edge.Dyndep != nil && edge.Dyndep.DyndepPending {
if err := g.dyndepLoader.LoadDyndeps(edge.Dyndep, DyndepFile{}); err != nil {
warningf("%s\n", err)
}
}
if len(edge.Inputs) == 1 && len(edge.Outputs) == 1 {
// Can draw simply.
// Note extra space before label text -- this is cosmetic and feels
// like a graphviz bug.
fmt.Fprintf(g.out, "\"%p\" -> \"%p\" [label=\" %s\"]\n", edge.Inputs[0], edge.Outputs[0], edge.Rule.Name)
} else {
fmt.Fprintf(g.out, "\"%p\" [label=\"%s\", shape=ellipse]\n", edge, edge.Rule.Name)
for _, out := range edge.Outputs {
fmt.Fprintf(g.out, "\"%p\" -> \"%p\"\n", edge, out)
}
for i, in := range edge.Inputs {
orderOnly := ""
if edge.IsOrderOnly(i) {
orderOnly = " style=dotted"
}
fmt.Fprintf(g.out, "\"%p\" -> \"%p\" [arrowhead=none%s]\n", in, edge, orderOnly)
}
}
for _, in := range edge.Inputs {
g.AddTarget(in)
}
}
// Start prints out the header.
func (g *GraphViz) Start() {
fmt.Fprintf(g.out, "digraph ninja {\n")
fmt.Fprintf(g.out, "rankdir=\"LR\"\n")
fmt.Fprintf(g.out, "node [fontsize=10, shape=box, height=0.25]\n")
fmt.Fprintf(g.out, "edge [fontsize=10]\n")
}
// Finish prints out the footer.
func (g *GraphViz) Finish() {
fmt.Fprintf(g.out, "}\n")
}