/
changes.go
82 lines (74 loc) · 2.57 KB
/
changes.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
package query
import (
"bytes"
"fmt"
"os/exec"
"sort"
"github.com/google/shlex"
"build"
"core"
)
// MustCheckout checks out the given revision.
func MustCheckout(revision, command string) {
log.Notice("Checking out %s...", revision)
if argv, err := shlex.Split(fmt.Sprintf(command, revision)); err != nil {
log.Fatalf("Invalid checkout command: %s", err)
} else if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil {
log.Fatalf("Failed to check out %s: %s\n%s", revision, err, out)
}
}
// MustGetRevision runs a command to determine the current revision.
func MustGetRevision(command string) string {
log.Notice("Determining current revision...")
argv, err := shlex.Split(command)
if err != nil {
log.Fatalf("Invalid revision command: %s", err)
}
out, err := exec.Command(argv[0], argv[1:]...).Output()
if err != nil {
log.Fatalf("Failed to determine current revision: %s\n%s", err, out)
}
return string(bytes.TrimSpace(out))
}
// DiffGraphs calculates the difference between two build graphs.
// Note that this is not symmetric; targets that have been removed from 'before' do not appear
// (because this is designed to be fed into 'plz test' and we can't test targets that no longer exist).
func DiffGraphs(before, after *core.BuildState, files []string) []core.BuildLabel {
log.Notice("Calculating difference...")
configChanged := !bytes.Equal(before.Hashes.Config, after.Hashes.Config)
targets := after.Graph.AllTargets()
done := make(map[*core.BuildTarget]struct{}, len(targets))
for _, t2 := range targets {
if t1 := before.Graph.Target(t2.Label); t1 == nil || changed(before, after, t1, t2, files) || configChanged {
if after.ShouldInclude(t2) {
addRevdeps(after.Graph, t2, done)
}
}
}
ret := make(core.BuildLabels, 0, len(done))
for target := range done {
ret = append(ret, target.Label)
}
sort.Sort(ret)
return ret
}
// changed returns true if the given two targets are not equivalent.
func changed(s1, s2 *core.BuildState, t1, t2 *core.BuildTarget, files []string) bool {
for _, f := range files {
if t2.HasSource(f) {
return true
}
}
h1 := build.RuleHash(s1, t1, true, false)
h2 := build.RuleHash(s2, t2, true, false)
return !bytes.Equal(h1, h2)
}
// addRevdeps walks back up the reverse dependencies of a target, marking them all changed.
func addRevdeps(graph *core.BuildGraph, target *core.BuildTarget, done map[*core.BuildTarget]struct{}) {
if _, present := done[target]; !present {
done[target] = struct{}{}
for _, revdep := range graph.ReverseDependencies(target) {
addRevdeps(graph, revdep, done)
}
}
}