// Set dependency pointers on all contents of the graph. // Has to be done after to test cycles etc. func updateDependencies(graph *core.BuildGraph) { for _, target := range graph.AllTargets() { for _, dep := range target.DeclaredDependencies() { graph.AddDependency(target.Label, dep) } } }
// QueryAffectedTargets 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 QueryAffectedTargets(graph *core.BuildGraph, 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 graph.AllTargets() { for _, source := range target.AllSourcePaths(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.Targets { affectedTargets <- target } } for _, pkg := range graph.PackageMap() { if _, present := filePaths[pkg.Filename]; present { invalidatePackage(pkg) } else { for _, subinclude := range pkg.Subincludes { for _, source := range graph.TargetOrDie(subinclude).AllSourcePaths(graph) { if _, present := filePaths[source]; present { invalidatePackage(pkg) break } } } } } done <- true }() go handleAffectedTargets(graph, affectedTargets, done, include, exclude, tests, transitive) <-done <-done close(affectedTargets) <-done }
// Prints all targets in the build graph that are marked to be built but not built yet. func unbuiltTargetsMessage(graph *core.BuildGraph) string { msg := "" for _, target := range graph.AllTargets() { if target.State() == core.Active { if graph.AllDepsBuilt(target) { msg += fmt.Sprintf(" %s (waiting for deps to build)\n", target.Label) } else { msg += fmt.Sprintf(" %s\n", target.Label) } } else if target.State() == core.Pending { msg += fmt.Sprintf(" %s (pending build)\n", target.Label) } } if msg != "" { return "\nThe following targets have not yet built:\n" + msg } return "" }
// Write test results out to a file in xUnit format. Dies on any errors. func WriteResultsToFileOrDie(graph *core.BuildGraph, filename string) { if err := os.MkdirAll(path.Dir(filename), core.DirPermissions); err != nil { log.Fatalf("Failed to create directory for test output") } results := JUnitXMLTestResults{} results.XMLName.Local = "testsuites" for _, target := range graph.AllTargets() { if target.Results.NumTests > 0 { suite := JUnitXMLTestSuite{ Name: target.Label.String(), Failures: target.Results.Failed, Tests: target.Results.NumTests, } for _, pass := range target.Results.Passes { suite.TestCases = append(suite.TestCases, JUnitXMLTest{Name: pass}) } for _, fail := range target.Results.Failures { suite.TestCases = append(suite.TestCases, JUnitXMLTest{ Name: fail.Name, Type: fail.Type, Stdout: fail.Stdout, Stderr: fail.Stderr, Error: &JUnitXMLFailure{ Type: fail.Type, Traceback: fail.Traceback, }, }) } results.TestSuites = append(results.TestSuites, suite) } } if b, err := xml.MarshalIndent(results, "", " "); err != nil { log.Fatalf("Failed to serialise XML: %s", err) } else if err = ioutil.WriteFile(filename, b, 0644); err != nil { log.Fatalf("Failed to write XML to %s: %s", filename, err) } }