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  }