diff options
Diffstat (limited to 'bitbake/lib/bb/build.py')
-rw-r--r-- | bitbake/lib/bb/build.py | 667 |
1 files changed, 667 insertions, 0 deletions
diff --git a/bitbake/lib/bb/build.py b/bitbake/lib/bb/build.py new file mode 100644 index 0000000000..2e49a09365 --- /dev/null +++ b/bitbake/lib/bb/build.py | |||
@@ -0,0 +1,667 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # BitBake 'Build' implementation | ||
5 | # | ||
6 | # Core code for function execution and task handling in the | ||
7 | # BitBake build tools. | ||
8 | # | ||
9 | # Copyright (C) 2003, 2004 Chris Larson | ||
10 | # | ||
11 | # Based on Gentoo's portage.py. | ||
12 | # | ||
13 | # This program is free software; you can redistribute it and/or modify | ||
14 | # it under the terms of the GNU General Public License version 2 as | ||
15 | # published by the Free Software Foundation. | ||
16 | # | ||
17 | # This program is distributed in the hope that it will be useful, | ||
18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | # GNU General Public License for more details. | ||
21 | # | ||
22 | # You should have received a copy of the GNU General Public License along | ||
23 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
24 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
25 | # | ||
26 | #Based on functions from the base bb module, Copyright 2003 Holger Schurig | ||
27 | |||
28 | import os | ||
29 | import sys | ||
30 | import logging | ||
31 | import shlex | ||
32 | import glob | ||
33 | import bb | ||
34 | import bb.msg | ||
35 | import bb.process | ||
36 | from contextlib import nested | ||
37 | from bb import event, utils | ||
38 | |||
39 | bblogger = logging.getLogger('BitBake') | ||
40 | logger = logging.getLogger('BitBake.Build') | ||
41 | |||
42 | NULL = open(os.devnull, 'r+') | ||
43 | |||
44 | |||
45 | # When we execute a python function we'd like certain things | ||
46 | # in all namespaces, hence we add them to __builtins__ | ||
47 | # If we do not do this and use the exec globals, they will | ||
48 | # not be available to subfunctions. | ||
49 | __builtins__['bb'] = bb | ||
50 | __builtins__['os'] = os | ||
51 | |||
52 | class FuncFailed(Exception): | ||
53 | def __init__(self, name = None, logfile = None): | ||
54 | self.logfile = logfile | ||
55 | self.name = name | ||
56 | if name: | ||
57 | self.msg = 'Function failed: %s' % name | ||
58 | else: | ||
59 | self.msg = "Function failed" | ||
60 | |||
61 | def __str__(self): | ||
62 | if self.logfile and os.path.exists(self.logfile): | ||
63 | msg = ("%s (log file is located at %s)" % | ||
64 | (self.msg, self.logfile)) | ||
65 | else: | ||
66 | msg = self.msg | ||
67 | return msg | ||
68 | |||
69 | class TaskBase(event.Event): | ||
70 | """Base class for task events""" | ||
71 | |||
72 | def __init__(self, t, logfile, d): | ||
73 | self._task = t | ||
74 | self._package = d.getVar("PF", True) | ||
75 | self.taskfile = d.getVar("FILE", True) | ||
76 | self.taskname = self._task | ||
77 | self.logfile = logfile | ||
78 | event.Event.__init__(self) | ||
79 | self._message = "recipe %s: task %s: %s" % (d.getVar("PF", True), t, self.getDisplayName()) | ||
80 | |||
81 | def getTask(self): | ||
82 | return self._task | ||
83 | |||
84 | def setTask(self, task): | ||
85 | self._task = task | ||
86 | |||
87 | def getDisplayName(self): | ||
88 | return bb.event.getName(self)[4:] | ||
89 | |||
90 | task = property(getTask, setTask, None, "task property") | ||
91 | |||
92 | class TaskStarted(TaskBase): | ||
93 | """Task execution started""" | ||
94 | |||
95 | class TaskSucceeded(TaskBase): | ||
96 | """Task execution completed""" | ||
97 | |||
98 | class TaskFailed(TaskBase): | ||
99 | """Task execution failed""" | ||
100 | |||
101 | def __init__(self, task, logfile, metadata, errprinted = False): | ||
102 | self.errprinted = errprinted | ||
103 | super(TaskFailed, self).__init__(task, logfile, metadata) | ||
104 | |||
105 | class TaskFailedSilent(TaskBase): | ||
106 | """Task execution failed (silently)""" | ||
107 | def getDisplayName(self): | ||
108 | # Don't need to tell the user it was silent | ||
109 | return "Failed" | ||
110 | |||
111 | class TaskInvalid(TaskBase): | ||
112 | |||
113 | def __init__(self, task, metadata): | ||
114 | super(TaskInvalid, self).__init__(task, None, metadata) | ||
115 | self._message = "No such task '%s'" % task | ||
116 | |||
117 | |||
118 | class LogTee(object): | ||
119 | def __init__(self, logger, outfile): | ||
120 | self.outfile = outfile | ||
121 | self.logger = logger | ||
122 | self.name = self.outfile.name | ||
123 | |||
124 | def write(self, string): | ||
125 | self.logger.plain(string) | ||
126 | self.outfile.write(string) | ||
127 | |||
128 | def __enter__(self): | ||
129 | self.outfile.__enter__() | ||
130 | return self | ||
131 | |||
132 | def __exit__(self, *excinfo): | ||
133 | self.outfile.__exit__(*excinfo) | ||
134 | |||
135 | def __repr__(self): | ||
136 | return '<LogTee {0}>'.format(self.name) | ||
137 | def flush(self): | ||
138 | self.outfile.flush() | ||
139 | |||
140 | def exec_func(func, d, dirs = None): | ||
141 | """Execute an BB 'function'""" | ||
142 | |||
143 | body = d.getVar(func) | ||
144 | if not body: | ||
145 | if body is None: | ||
146 | logger.warn("Function %s doesn't exist", func) | ||
147 | return | ||
148 | |||
149 | flags = d.getVarFlags(func) | ||
150 | cleandirs = flags.get('cleandirs') | ||
151 | if cleandirs: | ||
152 | for cdir in d.expand(cleandirs).split(): | ||
153 | bb.utils.remove(cdir, True) | ||
154 | |||
155 | if dirs is None: | ||
156 | dirs = flags.get('dirs') | ||
157 | if dirs: | ||
158 | dirs = d.expand(dirs).split() | ||
159 | |||
160 | if dirs: | ||
161 | for adir in dirs: | ||
162 | bb.utils.mkdirhier(adir) | ||
163 | adir = dirs[-1] | ||
164 | else: | ||
165 | adir = d.getVar('B', True) | ||
166 | bb.utils.mkdirhier(adir) | ||
167 | |||
168 | ispython = flags.get('python') | ||
169 | |||
170 | lockflag = flags.get('lockfiles') | ||
171 | if lockflag: | ||
172 | lockfiles = [d.expand(f) for f in lockflag.split()] | ||
173 | else: | ||
174 | lockfiles = None | ||
175 | |||
176 | tempdir = d.getVar('T', True) | ||
177 | |||
178 | # or func allows items to be executed outside of the normal | ||
179 | # task set, such as buildhistory | ||
180 | task = d.getVar('BB_RUNTASK', True) or func | ||
181 | if task == func: | ||
182 | taskfunc = task | ||
183 | else: | ||
184 | taskfunc = "%s.%s" % (task, func) | ||
185 | |||
186 | runfmt = d.getVar('BB_RUNFMT', True) or "run.{func}.{pid}" | ||
187 | runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid()) | ||
188 | runfile = os.path.join(tempdir, runfn) | ||
189 | bb.utils.mkdirhier(os.path.dirname(runfile)) | ||
190 | |||
191 | # Setup the courtesy link to the runfn, only for tasks | ||
192 | # we create the link 'just' before the run script is created | ||
193 | # if we create it after, and if the run script fails, then the | ||
194 | # link won't be created as an exception would be fired. | ||
195 | if task == func: | ||
196 | runlink = os.path.join(tempdir, 'run.{0}'.format(task)) | ||
197 | if runlink: | ||
198 | bb.utils.remove(runlink) | ||
199 | |||
200 | try: | ||
201 | os.symlink(runfn, runlink) | ||
202 | except OSError: | ||
203 | pass | ||
204 | |||
205 | with bb.utils.fileslocked(lockfiles): | ||
206 | if ispython: | ||
207 | exec_func_python(func, d, runfile, cwd=adir) | ||
208 | else: | ||
209 | exec_func_shell(func, d, runfile, cwd=adir) | ||
210 | |||
211 | _functionfmt = """ | ||
212 | def {function}(d): | ||
213 | {body} | ||
214 | |||
215 | {function}(d) | ||
216 | """ | ||
217 | logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s") | ||
218 | def exec_func_python(func, d, runfile, cwd=None): | ||
219 | """Execute a python BB 'function'""" | ||
220 | |||
221 | bbfile = d.getVar('FILE', True) | ||
222 | code = _functionfmt.format(function=func, body=d.getVar(func, True)) | ||
223 | bb.utils.mkdirhier(os.path.dirname(runfile)) | ||
224 | with open(runfile, 'w') as script: | ||
225 | script.write(code) | ||
226 | |||
227 | if cwd: | ||
228 | try: | ||
229 | olddir = os.getcwd() | ||
230 | except OSError: | ||
231 | olddir = None | ||
232 | os.chdir(cwd) | ||
233 | |||
234 | bb.debug(2, "Executing python function %s" % func) | ||
235 | |||
236 | try: | ||
237 | comp = utils.better_compile(code, func, bbfile) | ||
238 | utils.better_exec(comp, {"d": d}, code, bbfile) | ||
239 | except: | ||
240 | if sys.exc_info()[0] in (bb.parse.SkipPackage, bb.build.FuncFailed): | ||
241 | raise | ||
242 | |||
243 | raise FuncFailed(func, None) | ||
244 | finally: | ||
245 | bb.debug(2, "Python function %s finished" % func) | ||
246 | |||
247 | if cwd and olddir: | ||
248 | try: | ||
249 | os.chdir(olddir) | ||
250 | except OSError: | ||
251 | pass | ||
252 | |||
253 | def exec_func_shell(func, d, runfile, cwd=None): | ||
254 | """Execute a shell function from the metadata | ||
255 | |||
256 | Note on directory behavior. The 'dirs' varflag should contain a list | ||
257 | of the directories you need created prior to execution. The last | ||
258 | item in the list is where we will chdir/cd to. | ||
259 | """ | ||
260 | |||
261 | # Don't let the emitted shell script override PWD | ||
262 | d.delVarFlag('PWD', 'export') | ||
263 | |||
264 | with open(runfile, 'w') as script: | ||
265 | script.write('''#!/bin/sh\n | ||
266 | # Emit a useful diagnostic if something fails: | ||
267 | bb_exit_handler() { | ||
268 | ret=$? | ||
269 | case $ret in | ||
270 | 0) ;; | ||
271 | *) case $BASH_VERSION in | ||
272 | "") echo "WARNING: exit code $ret from a shell command.";; | ||
273 | *) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from | ||
274 | \"$BASH_COMMAND\"";; | ||
275 | esac | ||
276 | exit $ret | ||
277 | esac | ||
278 | } | ||
279 | trap 'bb_exit_handler' 0 | ||
280 | set -e | ||
281 | ''') | ||
282 | |||
283 | bb.data.emit_func(func, script, d) | ||
284 | |||
285 | if bb.msg.loggerVerboseLogs: | ||
286 | script.write("set -x\n") | ||
287 | if cwd: | ||
288 | script.write("cd %s\n" % cwd) | ||
289 | script.write("%s\n" % func) | ||
290 | script.write(''' | ||
291 | # cleanup | ||
292 | ret=$? | ||
293 | trap '' 0 | ||
294 | exit $? | ||
295 | ''') | ||
296 | |||
297 | os.chmod(runfile, 0775) | ||
298 | |||
299 | cmd = runfile | ||
300 | if d.getVarFlag(func, 'fakeroot'): | ||
301 | fakerootcmd = d.getVar('FAKEROOT', True) | ||
302 | if fakerootcmd: | ||
303 | cmd = [fakerootcmd, runfile] | ||
304 | |||
305 | if bb.msg.loggerDefaultVerbose: | ||
306 | logfile = LogTee(logger, sys.stdout) | ||
307 | else: | ||
308 | logfile = sys.stdout | ||
309 | |||
310 | bb.debug(2, "Executing shell function %s" % func) | ||
311 | |||
312 | try: | ||
313 | with open(os.devnull, 'r+') as stdin: | ||
314 | bb.process.run(cmd, shell=False, stdin=stdin, log=logfile) | ||
315 | except bb.process.CmdError: | ||
316 | logfn = d.getVar('BB_LOGFILE', True) | ||
317 | raise FuncFailed(func, logfn) | ||
318 | |||
319 | bb.debug(2, "Shell function %s finished" % func) | ||
320 | |||
321 | def _task_data(fn, task, d): | ||
322 | localdata = bb.data.createCopy(d) | ||
323 | localdata.setVar('BB_FILENAME', fn) | ||
324 | localdata.setVar('BB_CURRENTTASK', task[3:]) | ||
325 | localdata.setVar('OVERRIDES', 'task-%s:%s' % | ||
326 | (task[3:], d.getVar('OVERRIDES', False))) | ||
327 | localdata.finalize() | ||
328 | bb.data.expandKeys(localdata) | ||
329 | return localdata | ||
330 | |||
331 | def _exec_task(fn, task, d, quieterr): | ||
332 | """Execute a BB 'task' | ||
333 | |||
334 | Execution of a task involves a bit more setup than executing a function, | ||
335 | running it with its own local metadata, and with some useful variables set. | ||
336 | """ | ||
337 | if not d.getVarFlag(task, 'task'): | ||
338 | event.fire(TaskInvalid(task, d), d) | ||
339 | logger.error("No such task: %s" % task) | ||
340 | return 1 | ||
341 | |||
342 | logger.debug(1, "Executing task %s", task) | ||
343 | |||
344 | localdata = _task_data(fn, task, d) | ||
345 | tempdir = localdata.getVar('T', True) | ||
346 | if not tempdir: | ||
347 | bb.fatal("T variable not set, unable to build") | ||
348 | |||
349 | # Change nice level if we're asked to | ||
350 | nice = localdata.getVar("BB_TASK_NICE_LEVEL", True) | ||
351 | if nice: | ||
352 | curnice = os.nice(0) | ||
353 | nice = int(nice) - curnice | ||
354 | newnice = os.nice(nice) | ||
355 | logger.debug(1, "Renice to %s " % newnice) | ||
356 | |||
357 | bb.utils.mkdirhier(tempdir) | ||
358 | |||
359 | # Determine the logfile to generate | ||
360 | logfmt = localdata.getVar('BB_LOGFMT', True) or 'log.{task}.{pid}' | ||
361 | logbase = logfmt.format(task=task, pid=os.getpid()) | ||
362 | |||
363 | # Document the order of the tasks... | ||
364 | logorder = os.path.join(tempdir, 'log.task_order') | ||
365 | try: | ||
366 | with open(logorder, 'a') as logorderfile: | ||
367 | logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase)) | ||
368 | except OSError: | ||
369 | logger.exception("Opening log file '%s'", logorder) | ||
370 | pass | ||
371 | |||
372 | # Setup the courtesy link to the logfn | ||
373 | loglink = os.path.join(tempdir, 'log.{0}'.format(task)) | ||
374 | logfn = os.path.join(tempdir, logbase) | ||
375 | if loglink: | ||
376 | bb.utils.remove(loglink) | ||
377 | |||
378 | try: | ||
379 | os.symlink(logbase, loglink) | ||
380 | except OSError: | ||
381 | pass | ||
382 | |||
383 | prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True) | ||
384 | postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True) | ||
385 | |||
386 | class ErrorCheckHandler(logging.Handler): | ||
387 | def __init__(self): | ||
388 | self.triggered = False | ||
389 | logging.Handler.__init__(self, logging.ERROR) | ||
390 | def emit(self, record): | ||
391 | self.triggered = True | ||
392 | |||
393 | # Handle logfiles | ||
394 | si = open('/dev/null', 'r') | ||
395 | try: | ||
396 | bb.utils.mkdirhier(os.path.dirname(logfn)) | ||
397 | logfile = open(logfn, 'w') | ||
398 | except OSError: | ||
399 | logger.exception("Opening log file '%s'", logfn) | ||
400 | pass | ||
401 | |||
402 | # Dup the existing fds so we dont lose them | ||
403 | osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()] | ||
404 | oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()] | ||
405 | ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()] | ||
406 | |||
407 | # Replace those fds with our own | ||
408 | os.dup2(si.fileno(), osi[1]) | ||
409 | os.dup2(logfile.fileno(), oso[1]) | ||
410 | os.dup2(logfile.fileno(), ose[1]) | ||
411 | |||
412 | # Ensure python logging goes to the logfile | ||
413 | handler = logging.StreamHandler(logfile) | ||
414 | handler.setFormatter(logformatter) | ||
415 | # Always enable full debug output into task logfiles | ||
416 | handler.setLevel(logging.DEBUG - 2) | ||
417 | bblogger.addHandler(handler) | ||
418 | |||
419 | errchk = ErrorCheckHandler() | ||
420 | bblogger.addHandler(errchk) | ||
421 | |||
422 | localdata.setVar('BB_LOGFILE', logfn) | ||
423 | localdata.setVar('BB_RUNTASK', task) | ||
424 | |||
425 | event.fire(TaskStarted(task, logfn, localdata), localdata) | ||
426 | try: | ||
427 | for func in (prefuncs or '').split(): | ||
428 | exec_func(func, localdata) | ||
429 | exec_func(task, localdata) | ||
430 | for func in (postfuncs or '').split(): | ||
431 | exec_func(func, localdata) | ||
432 | except FuncFailed as exc: | ||
433 | if quieterr: | ||
434 | event.fire(TaskFailedSilent(task, logfn, localdata), localdata) | ||
435 | else: | ||
436 | errprinted = errchk.triggered | ||
437 | logger.error(str(exc)) | ||
438 | event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata) | ||
439 | return 1 | ||
440 | finally: | ||
441 | sys.stdout.flush() | ||
442 | sys.stderr.flush() | ||
443 | |||
444 | bblogger.removeHandler(handler) | ||
445 | |||
446 | # Restore the backup fds | ||
447 | os.dup2(osi[0], osi[1]) | ||
448 | os.dup2(oso[0], oso[1]) | ||
449 | os.dup2(ose[0], ose[1]) | ||
450 | |||
451 | # Close the backup fds | ||
452 | os.close(osi[0]) | ||
453 | os.close(oso[0]) | ||
454 | os.close(ose[0]) | ||
455 | si.close() | ||
456 | |||
457 | logfile.close() | ||
458 | if os.path.exists(logfn) and os.path.getsize(logfn) == 0: | ||
459 | logger.debug(2, "Zero size logfn %s, removing", logfn) | ||
460 | bb.utils.remove(logfn) | ||
461 | bb.utils.remove(loglink) | ||
462 | event.fire(TaskSucceeded(task, logfn, localdata), localdata) | ||
463 | |||
464 | if not localdata.getVarFlag(task, 'nostamp') and not localdata.getVarFlag(task, 'selfstamp'): | ||
465 | make_stamp(task, localdata) | ||
466 | |||
467 | return 0 | ||
468 | |||
469 | def exec_task(fn, task, d, profile = False): | ||
470 | try: | ||
471 | quieterr = False | ||
472 | if d.getVarFlag(task, "quieterrors") is not None: | ||
473 | quieterr = True | ||
474 | |||
475 | if profile: | ||
476 | profname = "profile-%s.log" % (d.getVar("PN", True) + "-" + task) | ||
477 | try: | ||
478 | import cProfile as profile | ||
479 | except: | ||
480 | import profile | ||
481 | prof = profile.Profile() | ||
482 | ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr) | ||
483 | prof.dump_stats(profname) | ||
484 | bb.utils.process_profilelog(profname) | ||
485 | |||
486 | return ret | ||
487 | else: | ||
488 | return _exec_task(fn, task, d, quieterr) | ||
489 | |||
490 | except Exception: | ||
491 | from traceback import format_exc | ||
492 | if not quieterr: | ||
493 | logger.error("Build of %s failed" % (task)) | ||
494 | logger.error(format_exc()) | ||
495 | failedevent = TaskFailed(task, None, d, True) | ||
496 | event.fire(failedevent, d) | ||
497 | return 1 | ||
498 | |||
499 | def stamp_internal(taskname, d, file_name): | ||
500 | """ | ||
501 | Internal stamp helper function | ||
502 | Makes sure the stamp directory exists | ||
503 | Returns the stamp path+filename | ||
504 | |||
505 | In the bitbake core, d can be a CacheData and file_name will be set. | ||
506 | When called in task context, d will be a data store, file_name will not be set | ||
507 | """ | ||
508 | taskflagname = taskname | ||
509 | if taskname.endswith("_setscene") and taskname != "do_setscene": | ||
510 | taskflagname = taskname.replace("_setscene", "") | ||
511 | |||
512 | if file_name: | ||
513 | stamp = d.stamp_base[file_name].get(taskflagname) or d.stamp[file_name] | ||
514 | extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or "" | ||
515 | else: | ||
516 | stamp = d.getVarFlag(taskflagname, 'stamp-base', True) or d.getVar('STAMP', True) | ||
517 | file_name = d.getVar('BB_FILENAME', True) | ||
518 | extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or "" | ||
519 | |||
520 | if not stamp: | ||
521 | return | ||
522 | |||
523 | stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo) | ||
524 | |||
525 | stampdir = os.path.dirname(stamp) | ||
526 | if bb.parse.cached_mtime_noerror(stampdir) == 0: | ||
527 | bb.utils.mkdirhier(stampdir) | ||
528 | |||
529 | return stamp | ||
530 | |||
531 | def stamp_cleanmask_internal(taskname, d, file_name): | ||
532 | """ | ||
533 | Internal stamp helper function to generate stamp cleaning mask | ||
534 | Returns the stamp path+filename | ||
535 | |||
536 | In the bitbake core, d can be a CacheData and file_name will be set. | ||
537 | When called in task context, d will be a data store, file_name will not be set | ||
538 | """ | ||
539 | taskflagname = taskname | ||
540 | if taskname.endswith("_setscene") and taskname != "do_setscene": | ||
541 | taskflagname = taskname.replace("_setscene", "") | ||
542 | |||
543 | if file_name: | ||
544 | stamp = d.stamp_base_clean[file_name].get(taskflagname) or d.stampclean[file_name] | ||
545 | extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or "" | ||
546 | else: | ||
547 | stamp = d.getVarFlag(taskflagname, 'stamp-base-clean', True) or d.getVar('STAMPCLEAN', True) | ||
548 | file_name = d.getVar('BB_FILENAME', True) | ||
549 | extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or "" | ||
550 | |||
551 | if not stamp: | ||
552 | return [] | ||
553 | |||
554 | cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo) | ||
555 | |||
556 | return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")] | ||
557 | |||
558 | def make_stamp(task, d, file_name = None): | ||
559 | """ | ||
560 | Creates/updates a stamp for a given task | ||
561 | (d can be a data dict or dataCache) | ||
562 | """ | ||
563 | cleanmask = stamp_cleanmask_internal(task, d, file_name) | ||
564 | for mask in cleanmask: | ||
565 | for name in glob.glob(mask): | ||
566 | # Preserve sigdata files in the stamps directory | ||
567 | if "sigdata" in name: | ||
568 | continue | ||
569 | # Preserve taint files in the stamps directory | ||
570 | if name.endswith('.taint'): | ||
571 | continue | ||
572 | os.unlink(name) | ||
573 | |||
574 | stamp = stamp_internal(task, d, file_name) | ||
575 | # Remove the file and recreate to force timestamp | ||
576 | # change on broken NFS filesystems | ||
577 | if stamp: | ||
578 | bb.utils.remove(stamp) | ||
579 | open(stamp, "w").close() | ||
580 | |||
581 | # If we're in task context, write out a signature file for each task | ||
582 | # as it completes | ||
583 | if not task.endswith("_setscene") and task != "do_setscene" and not file_name: | ||
584 | file_name = d.getVar('BB_FILENAME', True) | ||
585 | bb.parse.siggen.dump_sigtask(file_name, task, d.getVar('STAMP', True), True) | ||
586 | |||
587 | def del_stamp(task, d, file_name = None): | ||
588 | """ | ||
589 | Removes a stamp for a given task | ||
590 | (d can be a data dict or dataCache) | ||
591 | """ | ||
592 | stamp = stamp_internal(task, d, file_name) | ||
593 | bb.utils.remove(stamp) | ||
594 | |||
595 | def write_taint(task, d, file_name = None): | ||
596 | """ | ||
597 | Creates a "taint" file which will force the specified task and its | ||
598 | dependents to be re-run the next time by influencing the value of its | ||
599 | taskhash. | ||
600 | (d can be a data dict or dataCache) | ||
601 | """ | ||
602 | import uuid | ||
603 | if file_name: | ||
604 | taintfn = d.stamp[file_name] + '.' + task + '.taint' | ||
605 | else: | ||
606 | taintfn = d.getVar('STAMP', True) + '.' + task + '.taint' | ||
607 | bb.utils.mkdirhier(os.path.dirname(taintfn)) | ||
608 | # The specific content of the taint file is not really important, | ||
609 | # we just need it to be random, so a random UUID is used | ||
610 | with open(taintfn, 'w') as taintf: | ||
611 | taintf.write(str(uuid.uuid4())) | ||
612 | |||
613 | def stampfile(taskname, d, file_name = None): | ||
614 | """ | ||
615 | Return the stamp for a given task | ||
616 | (d can be a data dict or dataCache) | ||
617 | """ | ||
618 | return stamp_internal(taskname, d, file_name) | ||
619 | |||
620 | def add_tasks(tasklist, d): | ||
621 | task_deps = d.getVar('_task_deps') | ||
622 | if not task_deps: | ||
623 | task_deps = {} | ||
624 | if not 'tasks' in task_deps: | ||
625 | task_deps['tasks'] = [] | ||
626 | if not 'parents' in task_deps: | ||
627 | task_deps['parents'] = {} | ||
628 | |||
629 | for task in tasklist: | ||
630 | task = d.expand(task) | ||
631 | d.setVarFlag(task, 'task', 1) | ||
632 | |||
633 | if not task in task_deps['tasks']: | ||
634 | task_deps['tasks'].append(task) | ||
635 | |||
636 | flags = d.getVarFlags(task) | ||
637 | def getTask(name): | ||
638 | if not name in task_deps: | ||
639 | task_deps[name] = {} | ||
640 | if name in flags: | ||
641 | deptask = d.expand(flags[name]) | ||
642 | task_deps[name][task] = deptask | ||
643 | getTask('depends') | ||
644 | getTask('rdepends') | ||
645 | getTask('deptask') | ||
646 | getTask('rdeptask') | ||
647 | getTask('recrdeptask') | ||
648 | getTask('recideptask') | ||
649 | getTask('nostamp') | ||
650 | getTask('fakeroot') | ||
651 | getTask('noexec') | ||
652 | getTask('umask') | ||
653 | task_deps['parents'][task] = [] | ||
654 | if 'deps' in flags: | ||
655 | for dep in flags['deps']: | ||
656 | dep = d.expand(dep) | ||
657 | task_deps['parents'][task].append(dep) | ||
658 | |||
659 | # don't assume holding a reference | ||
660 | d.setVar('_task_deps', task_deps) | ||
661 | |||
662 | def remove_task(task, kill, d): | ||
663 | """Remove an BB 'task'. | ||
664 | |||
665 | If kill is 1, also remove tasks that depend on this task.""" | ||
666 | |||
667 | d.delVarFlag(task, 'task') | ||