annotate python/ppci/tasks.py @ 377:9667d78ba79e

Switched to xml for project description
author Windel Bouwman
date Fri, 11 Apr 2014 15:47:50 +0200
parents 3bb7dcfe5529
children 6ae782a085e0
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:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
28 """ A project contains a set of named targets that can depend upon
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:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
70 raise TaskError('Dependency loop detected {} -> {}'.format(target_name, dep))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
71 self.dfs(dep, state)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
72
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
73 def check_target(self, target_name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
74 state = set()
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
75 self.dfs(target_name, state)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
76
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
77 def dependencies(self, target_name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
78 assert type(target_name) is str
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
79 target = self.get_target(target_name)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
80 cdst = list(self.dependencies(dep) for dep in target.dependencies)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
81 cdst.append(target.dependencies)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
82 return set.union(*cdst)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
83
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 class Target:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
86 """ 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
87 def __init__(self, name, project):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
88 self.name = name
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
89 self.project = project
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
90 self.tasks = []
342
86b02c98a717 Moved target directory
Windel Bouwman
parents: 334
diff changeset
91 self.dependencies = set()
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
92
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
93 def add_task(self, task):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
94 self.tasks.append(task)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
95
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
96 def add_dependency(self, target_name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
97 """ Add another task as a dependency for this task """
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
98 self.dependencies.add(target_name)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
99
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
100 def __gt__(self, other):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
101 return other.name in self.project.dependencies(self.name)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
102
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
103 def __repr__(self):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
104 return 'Target "{}"'.format(self.name)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
105
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 class Task:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
108 """ Task that can run, and depend on other tasks """
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
109 def __init__(self, target, kwargs, sub_elements=[]):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
110 self.logger = logging.getLogger('task')
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
111 self.target = target
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
112 self.name = self.__class__.__name__
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
113 self.arguments = kwargs
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
114 self.subs = sub_elements
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
115
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
116 def get_argument(self, name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
117 if name not in self.arguments:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
118 raise TaskError('attribute "{}" not specified'.format(name))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
119 return self.arguments[name]
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
120
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
121 def get_property(self, name):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
122 return self.target.project.get_property(name)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
123
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
124 def relpath(self, filename):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
125 basedir = self.get_property('basedir')
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
126 return os.path.join(basedir, filename)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
127
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
128 def open_file_set(self, s):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
129 """ 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
130 - A string like "a.c3"
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
131 - A string like "*.c3"
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
132 - A string like "a.c3;src/*.c3"
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
133 """
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
134 assert type(s) is str
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
135 fns = []
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
136 for part in s.split(';'):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
137 fns += glob.glob(self.relpath(part))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
138 return fns
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
139
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
140 def run(self):
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
141 raise NotImplementedError("Implement this abstract method!")
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
142
332
87feb8a23b4d Added task list command
Windel Bouwman
parents: 329
diff changeset
143 def __repr__(self):
87feb8a23b4d Added task list command
Windel Bouwman
parents: 329
diff changeset
144 return 'Task "{}"'.format(self.name)
87feb8a23b4d Added task list command
Windel Bouwman
parents: 329
diff changeset
145
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
146
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
147 class TaskRunner:
332
87feb8a23b4d Added task list command
Windel Bouwman
parents: 329
diff changeset
148 """ Basic task runner that can run some tasks in sequence """
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
149 def __init__(self):
334
6f4753202b9a Added more recipes
Windel Bouwman
parents: 332
diff changeset
150 self.logger = logging.getLogger('taskrunner')
332
87feb8a23b4d Added task list command
Windel Bouwman
parents: 329
diff changeset
151
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
152 def run(self, project, targets=[]):
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
153 """ Try to run a project """
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
154 # Determine what targets to run:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
155 if targets:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
156 target_list = targets
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
157 else:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
158 if project.default:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
159 target_list = [project.default]
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
160 else:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
161 target_list = []
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
162
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
163 try:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
164 if not target_list:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
165 self.logger.info('Done!')
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
166 return 0
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
167
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
168 # Check for loops:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
169 for target in target_list:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
170 project.check_target(target)
334
6f4753202b9a Added more recipes
Windel Bouwman
parents: 332
diff changeset
171
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
172 # Calculate all dependencies:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
173 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
174 # Lookup actual targets:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
175 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
176 target_list.sort()
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
177
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
178 self.logger.info('Target sequence: {}'.format(target_list))
342
86b02c98a717 Moved target directory
Windel Bouwman
parents: 334
diff changeset
179
377
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
180 # Run tasks:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
181 for target in target_list:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
182 self.logger.info('Target {}'.format(target.name))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
183 for task in target.tasks:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
184 if type(task) is tuple:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
185 tname, props = task
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
186 for arg in props:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
187 props[arg] = project.expand_macros(props[arg])
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
188 task = task_map[tname](target, props)
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
189 self.logger.info('Running {}'.format(task))
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
190 task.run()
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
191 else:
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
192 raise Exception()
9667d78ba79e Switched to xml for project description
Windel Bouwman
parents: 346
diff changeset
193 self.logger.info('Done!')
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
194 except TaskError as e:
342
86b02c98a717 Moved target directory
Windel Bouwman
parents: 334
diff changeset
195 self.logger.error(str(e.msg))
329
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
196 return 1
8f6f3ace4e78 Added build tasks
Windel Bouwman
parents:
diff changeset
197 return 0
332
87feb8a23b4d Added task list command
Windel Bouwman
parents: 329
diff changeset
198