// activateTarget marks a target as active (ie. to be built) and adds its dependencies as pending parses. func activateTarget(state *core.BuildState, pkg *core.Package, label, dependor core.BuildLabel, noDeps bool, include, exclude []string) { if !label.IsAllTargets() && state.Graph.Target(label) == nil { msg := fmt.Sprintf("Parsed build file %s/BUILD but it doesn't contain target %s", label.PackageName, label.Name) if dependor != core.OriginalTarget { msg += fmt.Sprintf(" (depended on by %s)", dependor) } panic(msg + suggestTargets(pkg, label, dependor)) } if noDeps && !dependor.IsAllTargets() { // IsAllTargets indicates requirement for parse return // Some kinds of query don't need a full recursive parse. } else if label.IsAllTargets() { for _, target := range pkg.Targets { if target.ShouldInclude(include, exclude) { // Must always do this for coverage because we need to calculate sources of // non-test targets later on. if !state.NeedTests || target.IsTest || state.NeedCoverage { addDep(state, target.Label, dependor, false, dependor.IsAllTargets()) } } } } else { for _, l := range state.Graph.DependentTargets(dependor, label) { // We use :all to indicate a dependency needed for parse. addDep(state, l, dependor, false, dependor.IsAllTargets()) } } }
func addJSONTarget(graph *core.BuildGraph, ret *JSONGraph, label core.BuildLabel, done map[core.BuildLabel]struct{}) { if _, present := done[label]; present { return } done[label] = struct{}{} if label.IsAllTargets() { pkg := graph.PackageOrDie(label.PackageName) for _, target := range pkg.Targets { addJSONTarget(graph, ret, target.Label, done) } return } target := graph.TargetOrDie(label) if _, present := ret.Packages[label.PackageName]; present { ret.Packages[label.PackageName].Targets[label.Name] = makeJSONTarget(graph, target) } else { ret.Packages[label.PackageName] = JSONPackage{ Targets: map[string]JSONTarget{ label.Name: makeJSONTarget(graph, target), }, } } for _, dep := range target.Dependencies() { addJSONTarget(graph, ret, dep.Label, done) } }
func findOriginalTask(state *core.BuildState, target core.BuildLabel) { if target.IsAllSubpackages() { for pkg := range utils.FindAllSubpackages(state.Config, target.PackageName, "") { state.AddOriginalTarget(core.NewBuildLabel(pkg, "all")) } } else { state.AddOriginalTarget(target) } }
func querySomePath1(graph *core.BuildGraph, target1 *core.BuildTarget, label2 core.BuildLabel, print bool) bool { // Now we do the same for label2. if label2.IsAllTargets() { for _, target2 := range graph.PackageOrDie(label2.PackageName).Targets { if querySomePath2(graph, target1, target2, false) { return true } } return false } return querySomePath2(graph, target1, graph.TargetOrDie(label2), print) }
// QuerySomePath finds and returns a path between two targets. // Useful for a "why on earth do I depend on this thing" type query. func QuerySomePath(graph *core.BuildGraph, label1 core.BuildLabel, label2 core.BuildLabel) { // Awkwardly either target can be :all. This is an extremely useful idiom though so despite // trickiness is worth supporting. // Of course this calculation is also quadratic but it's not very obvious how to avoid that. if label1.IsAllTargets() { for _, target := range graph.PackageOrDie(label1.PackageName).Targets { if querySomePath1(graph, target, label2, false) { return } } fmt.Printf("Couldn't find any dependency path between %s and %s\n", label1, label2) } else { querySomePath1(graph, graph.TargetOrDie(label1), label2, true) } }
// Adds a single target to the build queue. func addDep(state *core.BuildState, label, dependor core.BuildLabel, rescan, forceBuild bool) { // Stop at any package that's not loaded yet if state.Graph.Package(label.PackageName) == nil { state.AddPendingParse(label, dependor, false) return } target := state.Graph.Target(label) if target == nil { log.Fatalf("Target %s (referenced by %s) doesn't exist\n", label, dependor) } if target.State() >= core.Active && !rescan && !forceBuild { return // Target is already tagged to be built and likely on the queue. } // Only do this bit if we actually need to build the target if !target.SyncUpdateState(core.Inactive, core.Semiactive) && !rescan && !forceBuild { return } if state.NeedBuild || forceBuild { if target.SyncUpdateState(core.Semiactive, core.Active) { state.AddActiveTarget() if target.IsTest && state.NeedTests { state.AddActiveTarget() // Tests count twice if we're gonna run them. } } } // If this target has no deps, add it to the queue now, otherwise handle its deps. // Only add if we need to build targets (not if we're just parsing) but we might need it to parse... if target.State() == core.Active && state.Graph.AllDepsBuilt(target) { if target.SyncUpdateState(core.Active, core.Pending) { state.AddPendingBuild(label, dependor.IsAllTargets()) } if !rescan { return } } for _, dep := range target.DeclaredDependencies() { // Check the require/provide stuff; we may need to add a different target. if len(target.Requires) > 0 { if depTarget := state.Graph.Target(dep); depTarget != nil && len(depTarget.Provides) > 0 { for _, provided := range depTarget.ProvideFor(target) { addDep(state, provided, label, false, forceBuild) } continue } } addDep(state, dep, label, false, forceBuild) } }
// DiffGraphs calculates the differences between two graphs. func DiffGraphs(before, after *query.JSONGraph, changedFiles, include, exclude []string, recurse bool) []core.BuildLabel { changedFileMap := toMap(changedFiles) allChanges := map[string]bool{} for pkgName, afterPkg := range after.Packages { beforePkg, present := before.Packages[pkgName] if !present { // Package didn't exist before, add every target in it. for targetName := range afterPkg.Targets { label := core.BuildLabel{PackageName: pkgName, Name: targetName} allChanges[label.String()] = true } continue } for targetName, afterTarget := range afterPkg.Targets { beforeTarget := beforePkg.Targets[targetName] if targetChanged(&beforeTarget, &afterTarget, pkgName, changedFileMap) { label := core.BuildLabel{PackageName: pkgName, Name: targetName} allChanges[label.String()] = true } } } // Now we have all the targets that are directly changed, we locate all transitive ones // in a second pass. We can't do this above because we've got no sensible ordering for it. ret := core.BuildLabels{} for pkgName, pkg := range after.Packages { for targetName, target := range pkg.Targets { if depsChanged(after, allChanges, pkgName, targetName, recurse) && shouldInclude(&target, include, exclude) { ret = append(ret, core.BuildLabel{PackageName: pkgName, Name: targetName}) } } } sort.Sort(ret) return ret }
func updateTarget(state *core.BuildState, plainOutput bool, buildingTarget *buildingTarget, label core.BuildLabel, active bool, failed bool, cached bool, description string, err error, colour string) { updateTarget2(buildingTarget, label, active, failed, cached, description, err, colour) if plainOutput { if failed { log.Errorf("%s: %s", label.String(), description) } else { if !active { active := pluralise(state.NumActive(), "task", "tasks") log.Notice("[%d/%s] %s: %s [%3.1fs]", state.NumDone(), active, label.String(), description, time.Now().Sub(buildingTarget.Started).Seconds()) } else { log.Info("%s: %s", label.String(), description) } } } }