github.com/sercand/please@v13.4.0+incompatible/src/query/changes.go (about) 1 package query 2 3 import ( 4 "bytes" 5 "fmt" 6 "os/exec" 7 "sort" 8 9 "github.com/google/shlex" 10 11 "github.com/thought-machine/please/src/build" 12 "github.com/thought-machine/please/src/core" 13 ) 14 15 // MustCheckout checks out the given revision. 16 func MustCheckout(revision, command string) { 17 log.Notice("Checking out %s...", revision) 18 if argv, err := shlex.Split(fmt.Sprintf(command, revision)); err != nil { 19 log.Fatalf("Invalid checkout command: %s", err) 20 } else if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil { 21 log.Fatalf("Failed to check out %s: %s\n%s", revision, err, out) 22 } 23 } 24 25 // MustGetRevision runs a command to determine the current revision. 26 func MustGetRevision(command string) string { 27 log.Notice("Determining current revision...") 28 argv, err := shlex.Split(command) 29 if err != nil { 30 log.Fatalf("Invalid revision command: %s", err) 31 } 32 out, err := exec.Command(argv[0], argv[1:]...).Output() 33 if err != nil { 34 log.Fatalf("Failed to determine current revision: %s\n%s", err, out) 35 } 36 return string(bytes.TrimSpace(out)) 37 } 38 39 // DiffGraphs calculates the difference between two build graphs. 40 // Note that this is not symmetric; targets that have been removed from 'before' do not appear 41 // (because this is designed to be fed into 'plz test' and we can't test targets that no longer exist). 42 func DiffGraphs(before, after *core.BuildState, files []string) []core.BuildLabel { 43 log.Notice("Calculating difference...") 44 configChanged := !bytes.Equal(before.Hashes.Config, after.Hashes.Config) 45 targets := after.Graph.AllTargets() 46 done := make(map[*core.BuildTarget]struct{}, len(targets)) 47 for _, t2 := range targets { 48 if t1 := before.Graph.Target(t2.Label); t1 == nil || changed(before, after, t1, t2, files) || configChanged { 49 addRevdeps(after, t2, done) 50 } 51 } 52 ret := make(core.BuildLabels, 0, len(done)) 53 for target := range done { 54 ret = append(ret, target.Label) 55 } 56 sort.Sort(ret) 57 return ret 58 } 59 60 // changed returns true if the given two targets are not equivalent. 61 func changed(s1, s2 *core.BuildState, t1, t2 *core.BuildTarget, files []string) bool { 62 for _, f := range files { 63 if t2.HasAbsoluteSource(f) { 64 return true 65 } 66 } 67 h1 := build.RuleHash(s1, t1, true, false) 68 h2 := build.RuleHash(s2, t2, true, false) 69 return !bytes.Equal(h1, h2) 70 } 71 72 // addRevdeps walks back up the reverse dependencies of a target, marking them all changed. 73 func addRevdeps(state *core.BuildState, target *core.BuildTarget, done map[*core.BuildTarget]struct{}) { 74 if _, present := done[target]; !present && state.ShouldInclude(target) { 75 done[target] = struct{}{} 76 for _, revdep := range state.Graph.ReverseDependencies(target) { 77 addRevdeps(state, revdep, done) 78 } 79 } 80 }