/
affected_targets.go
88 lines (79 loc) · 2.48 KB
/
affected_targets.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
package query
import "core"
import "fmt"
// AffectedTargets walks over the build graph and identifies all targets that have a transitive
// dependency on the given set of files.
// Targets are filtered by given include / exclude labels and if 'tests' is true only
// test targets will be returned.
func AffectedTargets(state *core.BuildState, files, include, exclude []string, tests, transitive bool) {
affectedTargets := make(chan *core.BuildTarget, 100)
done := make(chan bool)
filePaths := map[string]bool{}
for _, file := range files {
filePaths[file] = true
}
// Check all the targets to see if any own one of these files
go func() {
for _, target := range state.Graph.AllTargets() {
for _, source := range target.AllSourcePaths(state.Graph) {
if _, present := filePaths[source]; present {
affectedTargets <- target
break
}
}
}
done <- true
}()
// Check all the packages to see if any are defined by these files.
// This is pretty pessimistic, we have to just assume the whole package is invalidated.
// A better approach involves using plz query graph and plz_diff_graphs - see that tool
// for more explanation.
go func() {
invalidatePackage := func(pkg *core.Package) {
for _, target := range pkg.AllTargets() {
affectedTargets <- target
}
}
for _, pkg := range state.Graph.PackageMap() {
if _, present := filePaths[pkg.Filename]; present {
invalidatePackage(pkg)
} else {
for _, subinclude := range pkg.Subincludes {
for _, source := range state.Graph.TargetOrDie(subinclude).AllSourcePaths(state.Graph) {
if _, present := filePaths[source]; present {
invalidatePackage(pkg)
break
}
}
}
}
}
done <- true
}()
go handleAffectedTargets(state, affectedTargets, done, include, exclude, tests, transitive)
<-done
<-done
close(affectedTargets)
<-done
}
func handleAffectedTargets(state *core.BuildState, affectedTargets <-chan *core.BuildTarget, done chan<- bool, include, exclude []string, tests, transitive bool) {
seenTargets := map[*core.BuildTarget]bool{}
var inner func(*core.BuildTarget)
inner = func(target *core.BuildTarget) {
if !seenTargets[target] {
seenTargets[target] = true
if transitive {
for _, revdep := range state.Graph.ReverseDependencies(target) {
inner(revdep)
}
}
if (!tests || target.IsTest) && state.ShouldInclude(target) {
fmt.Printf("%s\n", target.Label)
}
}
}
for target := range affectedTargets {
inner(target)
}
done <- true
}