github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/model/patch_dependencies.go (about) 1 package model 2 3 import "github.com/mongodb/grip" 4 5 type dependencyIncluder struct { 6 Project *Project 7 included map[TVPair]bool 8 } 9 10 // Include crawls the tasks represented by the combination of variants and tasks and 11 // add or removes tasks based on the dependency graph. Required and dependent tasks 12 // are added; tasks that depend on unpatchable tasks are pruned. New slices 13 // of variants and tasks are returned. 14 func (di *dependencyIncluder) Include(initialDeps []TVPair) []TVPair { 15 di.included = map[TVPair]bool{} 16 17 // handle each pairing, recursively adding and pruning based 18 // on the task's requirements and dependencies 19 for _, d := range initialDeps { 20 di.handle(d) 21 } 22 23 outPairs := []TVPair{} 24 for pair, shouldInclude := range di.included { 25 if shouldInclude { 26 outPairs = append(outPairs, pair) 27 } 28 } 29 return outPairs 30 } 31 32 // handle finds and includes all tasks that the given task/variant pair 33 // requires or depends on. Returns true if the task and all of its 34 // dependent/required tasks are patchable, false if they are not. 35 func (di *dependencyIncluder) handle(pair TVPair) bool { 36 if included, ok := di.included[pair]; ok { 37 // we've been here before, so don't redo work 38 return included 39 } 40 41 // we must load the BuildVariantTask for the task/variant pair, 42 // since it contains the full scope of dependency information 43 bvt := di.Project.FindTaskForVariant(pair.TaskName, pair.Variant) 44 if bvt == nil { 45 grip.Errorf("task %s does not exist in project %s", pair.TaskName, 46 di.Project.Identifier) 47 di.included[pair] = false 48 return false // task not found in project--skip it. 49 } 50 51 if patchable := bvt.Patchable; patchable != nil && !*patchable { 52 di.included[pair] = false 53 return false // task cannot be patched, so skip it 54 } 55 di.included[pair] = true 56 57 // queue up all requirements and dependencies for recursive inclusion 58 deps := append( 59 di.expandRequirements(pair, bvt.Requires), 60 di.expandDependencies(pair, bvt.DependsOn)...) 61 for _, dep := range deps { 62 if ok := di.handle(dep); !ok { 63 di.included[pair] = false 64 return false // task depends on an unpatchable task, so skip it 65 } 66 } 67 68 // we've reached a point where we know it is safe to include the current task 69 return true 70 } 71 72 // expandRequirements finds all tasks required by the current task/variant pair. 73 func (di *dependencyIncluder) expandRequirements(pair TVPair, reqs []TaskRequirement) []TVPair { 74 deps := []TVPair{} 75 for _, r := range reqs { 76 if r.Variant == AllVariants { 77 // the case where we depend on all variants for a task 78 for _, v := range di.Project.FindVariantsWithTask(r.Name) { 79 if v != pair.Variant { // skip current variant 80 deps = append(deps, TVPair{TaskName: r.Name, Variant: v}) 81 } 82 } 83 } else { 84 // otherwise we're depending on a single task for a single variant 85 // We simply add a single task/variant and its dependencies. 86 v := r.Variant 87 if v == "" { 88 v = pair.Variant 89 } 90 deps = append(deps, TVPair{TaskName: r.Name, Variant: v}) 91 } 92 } 93 return deps 94 } 95 96 // expandRequirements finds all tasks depended on by the current task/variant pair. 97 func (di *dependencyIncluder) expandDependencies(pair TVPair, depends []TaskDependency) []TVPair { 98 deps := []TVPair{} 99 for _, d := range depends { 100 // don't automatically add dependencies if they are marked patch_optional 101 if d.PatchOptional { 102 continue 103 } 104 switch { 105 case d.Variant == AllVariants && d.Name == AllDependencies: // task = *, variant = * 106 // Here we get all variants and tasks (excluding the current task) 107 // and add them to the list of tasks and variants. 108 for _, v := range di.Project.FindAllVariants() { 109 for _, t := range di.Project.FindTasksForVariant(v) { 110 if !(t == pair.TaskName && v == pair.Variant) { 111 deps = append(deps, TVPair{TaskName: t, Variant: v}) 112 } 113 } 114 } 115 116 case d.Variant == AllVariants: // specific task, variant = * 117 // In the case where we depend on a task on all variants, we fetch the task's 118 // dependencies, then add that task for all variants that have it. 119 for _, v := range di.Project.FindVariantsWithTask(d.Name) { 120 if !(pair.TaskName == d.Name && pair.Variant == v) { 121 deps = append(deps, TVPair{TaskName: d.Name, Variant: v}) 122 } 123 } 124 125 case d.Name == AllDependencies: // task = *, specific variant 126 // Here we add every task for a single variant. We add the dependent variant, 127 // then add all of that variant's task, as well as their dependencies. 128 v := d.Variant 129 if v == "" { 130 v = pair.Variant 131 } 132 for _, t := range di.Project.FindTasksForVariant(v) { 133 if !(pair.TaskName == t && pair.Variant == v) { 134 deps = append(deps, TVPair{TaskName: t, Variant: v}) 135 } 136 } 137 138 default: // specific name, specific variant 139 // We simply add a single task/variant and its dependencies. 140 v := d.Variant 141 if v == "" { 142 v = pair.Variant 143 } 144 deps = append(deps, TVPair{TaskName: d.Name, Variant: v}) 145 } 146 } 147 return deps 148 }