1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """
18 ==================
19 Simple make base
20 ==================
21
22 Simple make base.
23 """
24 __author__ = u"Andr\xe9 Malo"
25 __docformat__ = "restructuredtext en"
26
27 import sys as _sys
28
29 from _setup import term as _term
30
31
33 """ Failure exception """
34
35
37 """ Fail for a reason """
38 raise Failure(reason)
39
40
41 -def warn(message, name=None):
42 """ Warn """
43 _term.red("%(NAME)sWarning: %(msg)s",
44 NAME=name and "%s:" % name or '', msg=message
45 )
46
47
49 """ Fatal error, immediate stop """
50 print >> _sys.stderr, reason
51 _sys.exit(1)
52
53
55 """ Target base class """
56 NAME = None
57 DEPS = None
58 HIDDEN = False
59
60 ERROR = None
61
63 """ Base __init__ """
64 self.runner = runner
65 self.init()
66
68 """ Default init hook """
69 pass
70
72 """ Default run hook """
73 pass
74
75 - def clean(self, scm=True, dist=False):
76 """ Default clean hook """
77 pass
78
79
81 """ Runner """
82
84 """ Initialization """
85 tdict = {}
86 if not targetscollection:
87 import __main__
88 targetscollection = [__main__]
89
90 from _setup.make import default_targets
91 if default_targets not in targetscollection:
92 targetscollection.append(default_targets)
93
94 for targets in targetscollection:
95 for value in vars(targets).values():
96 if isinstance(value, type) and issubclass(value, Target) and \
97 value.NAME is not None:
98 if value.NAME in tdict:
99 if issubclass(value, tdict[value.NAME]):
100 pass
101 elif issubclass(tdict[value.NAME], value):
102 continue
103 else:
104 warn('Ambigious target name', value.NAME)
105 continue
106 tdict[value.NAME] = value
107 self._tdict = tdict
108 self._itdict = {}
109
111 """ Print make help """
112 import textwrap as _textwrap
113
114 targets = self.targetinfo()
115 keys = []
116 for key, info in targets.items():
117 if not info['hide']:
118 keys.append(key)
119 keys.sort()
120 length = max(map(len, keys))
121 info = []
122 for key in keys:
123 info.append("%s%s" % (
124 (key + " " * length)[:length + 2],
125 _textwrap.fill(
126 targets[key]['desc'].strip(),
127 subsequent_indent=" " * (length + 2)
128 ),
129 ))
130 print "Available targets:\n\n" + "\n".join(info)
131
133 """ Extract target information """
134 result = {}
135 for name, cls in self._tdict.items():
136 result[name] = {
137 'desc': cls.__doc__ or "no description",
138 'hide': cls.HIDDEN,
139 'deps': cls.DEPS or (),
140 }
141 return result
142
144 """ Find all top level targets """
145 rev = {}
146 all_ = self.targetinfo()
147 for target, info in all_.items():
148 for dep in info['deps']:
149 if dep not in all_:
150 fatal("Unknown target '%s' (dep of %s) -> exit" % (
151 dep, target
152 ))
153 rev.setdefault(dep, []).append(target)
154 return [target for target, info in rev.items() if not info]
155
156 - def _run(self, target, seen=None):
157 """ Run a target """
158 if target.DEPS:
159 self(*target.DEPS, **{'seen': seen})
160
161 if not target.HIDDEN:
162 _term.yellow(">>> %(name)s", name=target.NAME)
163
164 try:
165 result = target.run()
166 except KeyboardInterrupt:
167 result, target.ERROR = False, "^C -> exit"
168 except Failure, e:
169 result, target.ERROR = False, "%s: %s" % (target.NAME, e)
170 except (SystemExit, MemoryError):
171 raise
172 except:
173 import traceback
174 target.ERROR = "%s errored:\n%s" % (target.NAME, ''.join(
175 traceback.format_exception(*_sys.exc_info())
176 ))
177 result = False
178 else:
179 if result is None:
180 result = True
181 return result
182
183 - def _clean(self, target, scm, dist, seen=None):
184 """ Run a target """
185 if target.DEPS:
186 self.run_clean(
187 *target.DEPS, **{'scm': scm, 'dist': dist, 'seen': seen}
188 )
189
190 try:
191 result = target.clean(scm, dist)
192 except KeyboardInterrupt:
193 result, target.ERROR = False, "^C -> exit"
194 except Failure, e:
195 result, target.ERROR = False, "%s: %s" % (target.NAME, e)
196 except (SystemExit, MemoryError):
197 raise
198 except:
199 import traceback
200 target.ERROR = "%s errored:\n%s" % (target.NAME, ''.join(
201 traceback.format_exception(*_sys.exc_info())
202 ))
203 result = False
204 else:
205 if result is None:
206 result = True
207 return result
208
210 """ Make init mapper """
211 def init(target):
212 """ Return initialized target """
213 if target not in seen:
214 try:
215 seen[target] = self._tdict[target](self)
216 except KeyError:
217 fatal("Unknown target '%s' -> exit" % target)
218 else:
219 seen[target] = None
220 return seen[target]
221 return init
222
224 """ Run targets """
225 def pop(name, default=None):
226 """ Pop """
227 if name in kwargs:
228 value = kwargs[name]
229 del kwargs[name]
230 if value is None:
231 return default
232 return value
233 else:
234 return default
235 seen = pop('seen', {})
236 scm = pop('scm', True)
237 dist = pop('dist', False)
238 if kwargs:
239 raise TypeError('Unknown keyword parameters')
240
241 if not targets:
242 top_targets = self._topleveltargets()
243 targets = self.targetinfo()
244 for item in top_targets:
245 del targets[item]
246 targets = targets.keys()
247 targets.sort()
248 top_targets.sort()
249 targets = top_targets + targets
250
251 init = self._make_init(seen)
252 for name in targets:
253 target = init(name)
254 if target is not None:
255 if not self._clean(target, scm=scm, dist=dist, seen=seen):
256 msg = target.ERROR
257 if msg is None:
258 msg = "Clean target %s returned error -> exit" % name
259 fatal(msg)
260
261 - def __call__(self, *targets, **kwargs):
262 """ Run targets """
263 if 'seen' in kwargs:
264 seen = kwargs['seen']
265 del kwargs['seen']
266 else:
267 seen = None
268 if seen is None:
269 seen = self._itdict
270 if kwargs:
271 raise TypeError('Unknown keyword parameters')
272
273 init = self._make_init(seen)
274 for name in targets:
275 target = init(name)
276 if target is not None:
277 if not self._run(target, seen):
278 msg = target.ERROR
279 if msg is None:
280 msg = "Target %s returned error -> exit" % name
281 fatal(msg)
282
283
284 -def main(*args, **kwargs):
285 """
286 main(argv=None, *args, name=None)
287
288 Main start point. This function parses the command line and executes the
289 targets given through `argv`. If there are no targets given, a help output
290 is generated.
291
292 :Parameters:
293 `argv` : sequence
294 Command line arguments. If omitted or ``None``, they are picked from
295 ``sys.argv``.
296
297 `args` : ``tuple``
298 The list of modules with targets. If omitted, ``__main__``
299 is imported and treated as target module. Additionally the mechanism
300 always adds the `_setup.make` module (this one) to the list in order
301 to grab some default targets.
302
303 `name` : ``str``
304 Name of the executing module. If omitted or ``None``, ``'__main__'``
305 is assumed. If the final name is not ``'__main__'``, the function
306 returns immediately.
307 """
308 try:
309 name = kwargs['name']
310 except KeyError:
311 name = '__main__'
312 else:
313 del kwargs['name']
314 if name is None:
315 name = '__main__'
316
317 try:
318 argv = kwargs['argv']
319 except KeyError:
320 if not args:
321 args = (None,)
322 else:
323 del kwargs['argv']
324 args = (argv,) + args
325
326 if kwargs:
327 raise TypeError("Unrecognized keyword arguments for main()")
328
329 if name == '__main__':
330 argv, args = args[0], args[1:]
331 if argv is None:
332 argv = _sys.argv[1:]
333
334 runner = _Runner(*args)
335 if argv:
336 runner(*argv)
337 else:
338 runner.print_help()
339