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  }