/
graph.go
148 lines (137 loc) · 4.72 KB
/
graph.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package query
import (
"encoding/base64"
"encoding/json"
"fmt"
"path"
"sync"
"build"
"core"
)
// Graph prints a representation of the build graph as JSON.
func Graph(state *core.BuildState, targets []core.BuildLabel) {
log.Notice("Generating graph...")
g := makeJSONGraph(state, targets)
log.Notice("Marshalling...")
b, err := json.MarshalIndent(g, "", " ")
if err != nil {
log.Fatalf("Failed to serialise JSON: %s\n", err)
}
log.Notice("Writing...")
fmt.Println(string(b))
log.Notice("Done")
}
// JSONGraph is an alternate representation of our build graph; will contain different information
// to the structures in core (also those ones can't be printed as JSON directly).
type JSONGraph struct {
Packages map[string]JSONPackage `json:"packages"`
}
// JSONPackage is an alternate representation of a build package
type JSONPackage struct {
name string
Targets map[string]JSONTarget `json:"targets"`
}
// JSONTarget is an alternate representation of a build target
type JSONTarget struct {
Inputs []string `json:"inputs,omitempty" note:"declared inputs of target"`
Outputs []string `json:"outs,omitempty" note:"corresponds to outs in rule declaration"`
Sources []string `json:"srcs,omitempty" note:"corresponds to srcs in rule declaration"`
Deps []string `json:"deps,omitempty" note:"corresponds to deps in rule declaration"`
Data []string `json:"data,omitempty" note:"corresponds to data in rule declaration"`
Labels []string `json:"labels,omitempty" note:"corresponds to labels in rule declaration"`
Requires []string `json:"requires,omitempty" note:"corresponds to requires in rule declaration"`
Hash string `json:"hash" note:"partial hash of target, does not include source hash"`
Test bool `json:"test,omitempty" note:"true if target is a test"`
Binary bool `json:"binary,omitempty" note:"true if target is a binary"`
TestOnly bool `json:"test_only,omitempty" note:"true if target should be restricted to test code"`
}
func makeJSONGraph(state *core.BuildState, targets []core.BuildLabel) *JSONGraph {
ret := JSONGraph{Packages: map[string]JSONPackage{}}
if len(targets) == 0 {
for pkg := range makeAllPackages(state) {
ret.Packages[pkg.name] = pkg
}
} else {
done := map[core.BuildLabel]struct{}{}
for _, target := range targets {
addJSONTarget(state, &ret, target, done)
}
}
return &ret
}
// makeAllPackages constructs all the JSONPackage objects for this graph in parallel.
func makeAllPackages(state *core.BuildState) <-chan JSONPackage {
ch := make(chan JSONPackage, 100)
go func() {
packages := state.Graph.PackageMap()
var wg sync.WaitGroup
wg.Add(len(packages))
for _, pkg := range packages {
go func(pkg *core.Package) {
ch <- makeJSONPackage(state, pkg)
wg.Done()
}(pkg)
}
wg.Wait()
close(ch)
}()
return ch
}
func addJSONTarget(state *core.BuildState, ret *JSONGraph, label core.BuildLabel, done map[core.BuildLabel]struct{}) {
if _, present := done[label]; present {
return
}
done[label] = struct{}{}
if label.IsAllTargets() {
pkg := state.Graph.PackageOrDie(label.PackageName)
for _, target := range pkg.AllTargets() {
addJSONTarget(state, ret, target.Label, done)
}
return
}
target := state.Graph.TargetOrDie(label)
if _, present := ret.Packages[label.PackageName]; present {
ret.Packages[label.PackageName].Targets[label.Name] = makeJSONTarget(state, target)
} else {
ret.Packages[label.PackageName] = JSONPackage{
Targets: map[string]JSONTarget{
label.Name: makeJSONTarget(state, target),
},
}
}
for _, dep := range target.Dependencies() {
addJSONTarget(state, ret, dep.Label, done)
}
}
func makeJSONPackage(state *core.BuildState, pkg *core.Package) JSONPackage {
targets := map[string]JSONTarget{}
for _, target := range pkg.AllTargets() {
targets[target.Label.Name] = makeJSONTarget(state, target)
}
return JSONPackage{name: pkg.Name, Targets: targets}
}
func makeJSONTarget(state *core.BuildState, target *core.BuildTarget) JSONTarget {
t := JSONTarget{
Sources: target.AllSourcePaths(state.Graph),
}
for in := range core.IterSources(state.Graph, target) {
t.Inputs = append(t.Inputs, in.Src)
}
for _, out := range target.Outputs() {
t.Outputs = append(t.Outputs, path.Join(target.Label.PackageName, out))
}
for _, dep := range target.Dependencies() {
t.Deps = append(t.Deps, dep.Label.String())
}
for data := range core.IterRuntimeFiles(state.Graph, target, false) {
t.Data = append(t.Data, data.Src)
}
t.Labels = target.Labels
t.Requires = target.Requires
rawHash := append(build.RuleHash(state, target, true, false), state.Hashes.Config...)
t.Hash = base64.RawStdEncoding.EncodeToString(rawHash)
t.Test = target.IsTest
t.Binary = target.IsBinary
t.TestOnly = target.TestOnly
return t
}