annotate python/ppci/tasks.py @ 397:5d03c10fe19d

Small changes
author Windel Bouwman
date Thu, 29 May 2014 10:47:28 +0200
parents 6ae782a085e0
children
rev   line source
342
86b02c98a717 Moved target directory
Windel Bouwman
parents: 334
diff changeset
1 """
86b02c98a717 Moved target directory
Windel Bouwman
parents: 334
diff changeset
2 This module defines tasks and a runner for these tasks. Tasks can
86b02c98a717 Moved target directory
Windel Bouwman
parents: 334
diff changeset
3 have dependencies and it can be determined if they need to be run.
86b02c98a717 Moved target directory
Windel Bouwman
parents: 334
diff changeset
4 """
334
6f4753202b9a Added more recipes
Windel Bouwman
parents: 332
diff changeset
5
6f4753202b9a Added more recipes
Windel Bouwman
parents: 332
diff changeset
6 import logging
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
7 import re
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
8 import os
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
9 import glob
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
10
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
11
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
12 task_map = {}
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
13 def register_task(name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
14 """ Decorator that registers a task class """
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
15 def f(cls):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
16 task_map[name] = cls
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
17 return cls
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
18 return f
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
19
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
20
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
21 class TaskError(Exception):
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
22 """ When a task fails, this exception is raised """
346
3bb7dcfe5529 expanded arm target
Windel Bouwman
parents: 342
diff changeset
23 def __init__(self, msg):
3bb7dcfe5529 expanded arm target
Windel Bouwman
parents: 342
diff changeset
24 self.msg = msg
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
25
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
26
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
27 class Project:
393
6ae782a085e0 Added init program
Windel Bouwman
parents: 377
diff changeset
28 """ A project contains a set of named targets that can depend upon
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
29 eachother """
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
30 def __init__(self, name):
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
31 self.name = name
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
32 self.targets = {}
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
33 self.properties = {}
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
34 self.macro_regex = re.compile('\$\{([^\}]+)\}')
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
35
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
36 def set_property(self, name, value):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
37 self.properties[name] = value
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
38
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
39 def get_property(self, name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
40 if name not in self.properties:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
41 raise TaskError('Property "{}" not found'.format(name))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
42 return self.properties[name]
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
43
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
44 def add_target(self, t):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
45 if t.name in self.targets:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
46 raise TaskError("Duplicate target '{}'".format(t.name))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
47 self.targets[t.name] = t
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
48
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
49 def get_target(self, target_name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
50 if target_name not in self.targets:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
51 raise TaskError('target "{}" not found'.format(target_name))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
52 return self.targets[target_name]
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
53
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
54 def expand_macros(self, txt):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
55 """ Replace all macros in txt with the correct properties """
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
56 while True:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
57 mo = self.macro_regex.search(txt)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
58 if not mo:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
59 break
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
60 propname = mo.group(1)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
61 propval = self.get_property(propname)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
62 txt = txt[:mo.start()] + propval + txt[mo.end():]
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
63 return txt
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
64
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
65 def dfs(self, target_name, state):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
66 state.add(target_name)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
67 target = self.get_target(target_name)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
68 for dep in target.dependencies:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
69 if dep in state:
393
6ae782a085e0 Added init program
Windel Bouwman
parents: 377
diff changeset
70 raise TaskError('Dependency loop detected {} -> {}'
6ae782a085e0 Added init program
Windel Bouwman
parents: 377
diff changeset
71 .format(target_name, dep))
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
72 self.dfs(dep, state)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
73
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
74 def check_target(self, target_name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
75 state = set()
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
76 self.dfs(target_name, state)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
77
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
78 def dependencies(self, target_name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
79 assert type(target_name) is str
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
80 target = self.get_target(target_name)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
81 cdst = list(self.dependencies(dep) for dep in target.dependencies)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
82 cdst.append(target.dependencies)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
83 return set.union(*cdst)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
84
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
85
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
86 class Target:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
87 """ Defines a target that has a name and a list of tasks to execute """
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
88 def __init__(self, name, project):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
89 self.name = name
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
90 self.project = project
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
91 self.tasks = []
342
86b02c98a717 Moved target directory
Windel Bouwman
parents: 334
diff changeset
92 self.dependencies = set()
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
93
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
94 def add_task(self, task):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
95 self.tasks.append(task)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
96
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
97 def add_dependency(self, target_name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
98 """ Add another task as a dependency for this task """
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
99 self.dependencies.add(target_name)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
100
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
101 def __gt__(self, other):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
102 return other.name in self.project.dependencies(self.name)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
103
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
104 def __repr__(self):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
105 return 'Target "{}"'.format(self.name)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
106
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
107
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
108 class Task:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
109 """ Task that can run, and depend on other tasks """
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
110 def __init__(self, target, kwargs, sub_elements=[]):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
111 self.logger = logging.getLogger('task')
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
112 self.target = target
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
113 self.name = self.__class__.__name__
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
114 self.arguments = kwargs
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
115 self.subs = sub_elements
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
116
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
117 def get_argument(self, name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
118 if name not in self.arguments:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
119 raise TaskError('attribute "{}" not specified'.format(name))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
120 return self.arguments[name]
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
121
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
122 def get_property(self, name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
123 return self.target.project.get_property(name)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
124
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
125 def relpath(self, filename):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
126 basedir = self.get_property('basedir')
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
127 return os.path.join(basedir, filename)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
128
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
129 def open_file_set(self, s):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
130 """ Creates a list of open file handles. s can be one of these:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
131 - A string like "a.c3"
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
132 - A string like "*.c3"
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
133 - A string like "a.c3;src/*.c3"
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
134 """
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
135 assert type(s) is str
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
136 fns = []
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
137 for part in s.split(';'):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
138 fns += glob.glob(self.relpath(part))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
139 return fns
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
140
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
141 def run(self):
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
142 raise NotImplementedError("Implement this abstract method!")
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
143
332
87feb8a23b4d Added task list command
Windel Bouwman
parents: 329
diff changeset
144 def __repr__(self):
87feb8a23b4d Added task list command
Windel Bouwman
parents: 329
diff changeset
145 return 'Task "{}"'.format(self.name)
87feb8a23b4d Added task list command
Windel Bouwman
parents: 329
diff changeset
146
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
147
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
148 class TaskRunner:
332
87feb8a23b4d Added task list command
Windel Bouwman
parents: 329
diff changeset
149 """ Basic task runner that can run some tasks in sequence """
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
150 def __init__(self):
334
6f4753202b9a Added more recipes
Windel Bouwman
parents: 332
diff changeset
151 self.logger = logging.getLogger('taskrunner')
332
87feb8a23b4d Added task list command
Windel Bouwman
parents: 329
diff changeset
152
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
153 def run(self, project, targets=[]):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
154 """ Try to run a project """
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
155 # Determine what targets to run:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
156 if targets:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
157 target_list = targets
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
158 else:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
159 if project.default:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
160 target_list = [project.default]
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
161 else:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
162 target_list = []
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
163
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
164 try:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
165 if not target_list:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
166 self.logger.info('Done!')
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
167 return 0
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
168
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
169 # Check for loops:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
170 for target in target_list:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
171 project.check_target(target)
334
6f4753202b9a Added more recipes
Windel Bouwman
parents: 332
diff changeset
172
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
173 # Calculate all dependencies:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
174 target_list = set.union(*[project.dependencies(t) for t in target_list]).union(set(target_list))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
175 # Lookup actual targets:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
176 target_list = [project.get_target(target_name) for target_name in target_list]
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
177 target_list.sort()
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
178
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
179 self.logger.info('Target sequence: {}'.format(target_list))
342
86b02c98a717 Moved target directory
Windel Bouwman
parents: 334
diff changeset
180
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
181 # Run tasks:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
182 for target in target_list:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
183 self.logger.info('Target {}'.format(target.name))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
184 for task in target.tasks:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
185 if type(task) is tuple:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
186 tname, props = task
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
187 for arg in props:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
188 props[arg] = project.expand_macros(props[arg])
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
189 task = task_map[tname](target, props)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
190 self.logger.info('Running {}'.format(task))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
191 task.run()
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
192 else:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
193 raise Exception()
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
194 self.logger.info('Done!')
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
195 except TaskError as e:
342
86b02c98a717 Moved target directory
Windel Bouwman
parents: 334
diff changeset
196 self.logger.error(str(e.msg))
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
197 return 1
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
198 return 0