1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5from __future__ import absolute_import, print_function, unicode_literals 6 7import attr 8 9 10@attr.s 11class Task(object): 12 """ 13 Representation of a task in a TaskGraph. Each Task has, at creation: 14 15 - kind: the name of the task kind 16 - label; the label for this task 17 - attributes: a dictionary of attributes for this task (used for filtering) 18 - task: the task definition (JSON-able dictionary) 19 - optimization: optimization to apply to the task (see taskgraph.optimize) 20 - dependencies: tasks this one depends on, in the form {name: label}, for example 21 {'build': 'build-linux64/opt', 'docker-image': 'docker-image-desktop-test'} 22 - soft_dependencies: tasks this one may depend on if they are available post 23 optimisation. They are set as a list of tasks label. 24 - if_dependencies: only run this task if at least one of these dependencies 25 are present. 26 27 And later, as the task-graph processing proceeds: 28 29 - task_id -- TaskCluster taskId under which this task will be created 30 31 This class is just a convenience wrapper for the data type and managing 32 display, comparison, serialization, etc. It has no functionality of its own. 33 """ 34 35 kind = attr.ib() 36 label = attr.ib() 37 attributes = attr.ib() 38 task = attr.ib() 39 description = attr.ib(default="") 40 task_id = attr.ib(default=None, init=False) 41 optimization = attr.ib(default=None) 42 dependencies = attr.ib(factory=dict) 43 soft_dependencies = attr.ib(factory=list) 44 if_dependencies = attr.ib(factory=list) 45 release_artifacts = attr.ib( 46 converter=attr.converters.optional(frozenset), 47 default=None, 48 ) 49 50 def __attrs_post_init__(self): 51 self.attributes["kind"] = self.kind 52 53 @property 54 def name(self): 55 if self.label.startswith(self.kind + "-"): 56 return self.label[len(self.kind) + 1 :] 57 else: 58 raise AttributeError("Task {} does not have a name.".format(self.label)) 59 60 def to_json(self): 61 rv = { 62 "kind": self.kind, 63 "label": self.label, 64 "description": self.description, 65 "attributes": self.attributes, 66 "dependencies": self.dependencies, 67 "soft_dependencies": sorted(self.soft_dependencies), 68 "if_dependencies": self.if_dependencies, 69 "optimization": self.optimization, 70 "task": self.task, 71 } 72 if self.task_id: 73 rv["task_id"] = self.task_id 74 if self.release_artifacts: 75 rv["release_artifacts"] = sorted(self.release_artifacts) 76 return rv 77 78 @classmethod 79 def from_json(cls, task_dict): 80 """ 81 Given a data structure as produced by taskgraph.to_json, re-construct 82 the original Task object. This is used to "resume" the task-graph 83 generation process, for example in Action tasks. 84 """ 85 rv = cls( 86 kind=task_dict["kind"], 87 label=task_dict["label"], 88 description=task_dict.get("description", ""), 89 attributes=task_dict["attributes"], 90 task=task_dict["task"], 91 optimization=task_dict["optimization"], 92 dependencies=task_dict.get("dependencies"), 93 soft_dependencies=task_dict.get("soft_dependencies"), 94 if_dependencies=task_dict.get("if_dependencies"), 95 release_artifacts=task_dict.get("release-artifacts"), 96 ) 97 if "task_id" in task_dict: 98 rv.task_id = task_dict["task_id"] 99 return rv 100