forked from mirrors/gecko-dev
		
	 e07398454b
			
		
	
	
		e07398454b
		
	
	
	
	
		
			
			Certain tasks are meant to perform follow-up work to another task. For example, a 'signing' task signs an artifact from a dependency task. An 'upload-symbols' tasks uploads an artifact from a dependency task. In general, we only want to run these types of tasks when the dependency task is running. But in practice, these tasks often cause the dependency to get pulled in. We've tried to fix this in the past by making the 'run-on-projects' and 'optimization' keys match their primary dependency. But it's very easy to mess this up, and some optimizations (like bugbug) take the content of the task definition into account. So even if both tasks use the same optimization, they could have different results. This revision adds a new 'if-dependencies' key that denotes a task that should only run if one of the dependencies specified in this list is also run. It will allow us to stop playing whack-a-mole when trying to make sure tasks aren't being pulled in solely due to these types of dependencies. This feature is implemented independently of the 'optimization', so tasks that specify this can still be optimized even if their primary dependency does run. Differential Revision: https://phabricator.services.mozilla.com/D80508
		
			
				
	
	
		
			101 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			101 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # This Source Code Form is subject to the terms of the Mozilla Public
 | |
| # License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
| # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | |
| 
 | |
| from __future__ import absolute_import, print_function, unicode_literals
 | |
| 
 | |
| import attr
 | |
| 
 | |
| 
 | |
| @attr.s
 | |
| class Task(object):
 | |
|     """
 | |
|     Representation of a task in a TaskGraph.  Each Task has, at creation:
 | |
| 
 | |
|     - kind: the name of the task kind
 | |
|     - label; the label for this task
 | |
|     - attributes: a dictionary of attributes for this task (used for filtering)
 | |
|     - task: the task definition (JSON-able dictionary)
 | |
|     - optimization: optimization to apply to the task (see taskgraph.optimize)
 | |
|     - dependencies: tasks this one depends on, in the form {name: label}, for example
 | |
|       {'build': 'build-linux64/opt', 'docker-image': 'build-docker-image-desktop-test'}
 | |
|     - soft_dependencies: tasks this one may depend on if they are available post
 | |
|       optimisation. They are set as a list of tasks label.
 | |
|     - if_dependencies: only run this task if at least one of these dependencies
 | |
|       are present.
 | |
| 
 | |
|     And later, as the task-graph processing proceeds:
 | |
| 
 | |
|     - task_id -- TaskCluster taskId under which this task will be created
 | |
| 
 | |
|     This class is just a convenience wrapper for the data type and managing
 | |
|     display, comparison, serialization, etc. It has no functionality of its own.
 | |
|     """
 | |
| 
 | |
|     kind = attr.ib()
 | |
|     label = attr.ib()
 | |
|     attributes = attr.ib()
 | |
|     task = attr.ib()
 | |
|     description = attr.ib(default="")
 | |
|     task_id = attr.ib(default=None, init=False)
 | |
|     optimization = attr.ib(default=None)
 | |
|     dependencies = attr.ib(factory=dict)
 | |
|     soft_dependencies = attr.ib(factory=list)
 | |
|     if_dependencies = attr.ib(factory=list)
 | |
|     release_artifacts = attr.ib(
 | |
|         converter=attr.converters.optional(frozenset),
 | |
|         default=None,
 | |
|     )
 | |
| 
 | |
|     def __attrs_post_init__(self):
 | |
|         self.attributes['kind'] = self.kind
 | |
| 
 | |
|     @property
 | |
|     def name(self):
 | |
|         if self.label.startswith(self.kind + "-"):
 | |
|             return self.label[len(self.kind)+1:]
 | |
|         elif self.label.startswith("build-docker-image-"):
 | |
|             return self.label[len("build-docker-image-"):]
 | |
|         else:
 | |
|             raise AttributeError("Task {} does not have a name.".format(self.label))
 | |
| 
 | |
|     def to_json(self):
 | |
|         rv = {
 | |
|             'kind': self.kind,
 | |
|             'label': self.label,
 | |
|             'description': self.description,
 | |
|             'attributes': self.attributes,
 | |
|             'dependencies': self.dependencies,
 | |
|             'soft_dependencies': sorted(self.soft_dependencies),
 | |
|             'if_dependencies': self.if_dependencies,
 | |
|             'optimization': self.optimization,
 | |
|             'task': self.task,
 | |
|         }
 | |
|         if self.task_id:
 | |
|             rv['task_id'] = self.task_id
 | |
|         if self.release_artifacts:
 | |
|             rv['release_artifacts'] = sorted(self.release_artifacts)
 | |
|         return rv
 | |
| 
 | |
|     @classmethod
 | |
|     def from_json(cls, task_dict):
 | |
|         """
 | |
|         Given a data structure as produced by taskgraph.to_json, re-construct
 | |
|         the original Task object.  This is used to "resume" the task-graph
 | |
|         generation process, for example in Action tasks.
 | |
|         """
 | |
|         rv = cls(
 | |
|             kind=task_dict['kind'],
 | |
|             label=task_dict['label'],
 | |
|             description=task_dict.get('description', ""),
 | |
|             attributes=task_dict['attributes'],
 | |
|             task=task_dict['task'],
 | |
|             optimization=task_dict['optimization'],
 | |
|             dependencies=task_dict.get('dependencies'),
 | |
|             soft_dependencies=task_dict.get('soft_dependencies'),
 | |
|             if_dependencies=task_dict.get('if_dependencies'),
 | |
|             release_artifacts=task_dict.get('release-artifacts'),
 | |
|         )
 | |
|         if 'task_id' in task_dict:
 | |
|             rv.task_id = task_dict['task_id']
 | |
|         return rv
 |