blob: db5072cb4d776e30679a74b94a2ef324cad249c3 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# 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
28import os
29import sys
30import logging
31import shlex
32import glob
33import time
34import stat
35import bb
36import bb.msg
37import bb.process
38from contextlib import nested
39from bb import event, utils
40
41bblogger = logging.getLogger('BitBake')
42logger = logging.getLogger('BitBake.Build')
43
44NULL = open(os.devnull, 'r+')
45
46__mtime_cache = {}
47
48def cached_mtime_noerror(f):
49 if f not in __mtime_cache:
50 try:
51 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
52 except OSError:
53 return 0
54 return __mtime_cache[f]
55
56def reset_cache():
57 global __mtime_cache
58 __mtime_cache = {}
59
60# When we execute a Python function, we'd like certain things
61# in all namespaces, hence we add them to __builtins__.
62# If we do not do this and use the exec globals, they will
63# not be available to subfunctions.
64__builtins__['bb'] = bb
65__builtins__['os'] = os
66
67class FuncFailed(Exception):
68 def __init__(self, name = None, logfile = None):
69 self.logfile = logfile
70 self.name = name
71 if name:
72 self.msg = 'Function failed: %s' % name
73 else:
74 self.msg = "Function failed"
75
76 def __str__(self):
77 if self.logfile and os.path.exists(self.logfile):
78 msg = ("%s (log file is located at %s)" %
79 (self.msg, self.logfile))
80 else:
81 msg = self.msg
82 return msg
83
84class TaskBase(event.Event):
85 """Base class for task events"""
86
87 def __init__(self, t, logfile, d):
88 self._task = t
89 self._package = d.getVar("PF", True)
90 self.taskfile = d.getVar("FILE", True)
91 self.taskname = self._task
92 self.logfile = logfile
93 self.time = time.time()
94 event.Event.__init__(self)
95 self._message = "recipe %s: task %s: %s" % (d.getVar("PF", True), t, self.getDisplayName())
96
97 def getTask(self):
98 return self._task
99
100 def setTask(self, task):
101 self._task = task
102
103 def getDisplayName(self):
104 return bb.event.getName(self)[4:]
105
106 task = property(getTask, setTask, None, "task property")
107
108class TaskStarted(TaskBase):
109 """Task execution started"""
110 def __init__(self, t, logfile, taskflags, d):
111 super(TaskStarted, self).__init__(t, logfile, d)
112 self.taskflags = taskflags
113
114class TaskSucceeded(TaskBase):
115 """Task execution completed"""
116
117class TaskFailed(TaskBase):
118 """Task execution failed"""
119
120 def __init__(self, task, logfile, metadata, errprinted = False):
121 self.errprinted = errprinted
122 super(TaskFailed, self).__init__(task, logfile, metadata)
123
124class TaskFailedSilent(TaskBase):
125 """Task execution failed (silently)"""
126 def getDisplayName(self):
127 # Don't need to tell the user it was silent
128 return "Failed"
129
130class TaskInvalid(TaskBase):
131
132 def __init__(self, task, metadata):
133 super(TaskInvalid, self).__init__(task, None, metadata)
134 self._message = "No such task '%s'" % task
135
136
137class LogTee(object):
138 def __init__(self, logger, outfile):
139 self.outfile = outfile
140 self.logger = logger
141 self.name = self.outfile.name
142
143 def write(self, string):
144 self.logger.plain(string)
145 self.outfile.write(string)
146
147 def __enter__(self):
148 self.outfile.__enter__()
149 return self
150
151 def __exit__(self, *excinfo):
152 self.outfile.__exit__(*excinfo)
153
154 def __repr__(self):
155 return '<LogTee {0}>'.format(self.name)
156 def flush(self):
157 self.outfile.flush()
158
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500159#
160# pythonexception allows the python exceptions generated to be raised
161# as the real exceptions (not FuncFailed) and without a backtrace at the
162# origin of the failure.
163#
164def exec_func(func, d, dirs = None, pythonexception=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500165 """Execute a BB 'function'"""
166
167 body = d.getVar(func, False)
168 if not body:
169 if body is None:
170 logger.warn("Function %s doesn't exist", func)
171 return
172
173 flags = d.getVarFlags(func)
174 cleandirs = flags.get('cleandirs')
175 if cleandirs:
176 for cdir in d.expand(cleandirs).split():
177 bb.utils.remove(cdir, True)
178 bb.utils.mkdirhier(cdir)
179
180 if dirs is None:
181 dirs = flags.get('dirs')
182 if dirs:
183 dirs = d.expand(dirs).split()
184
185 if dirs:
186 for adir in dirs:
187 bb.utils.mkdirhier(adir)
188 adir = dirs[-1]
189 else:
190 adir = d.getVar('B', True)
191 bb.utils.mkdirhier(adir)
192
193 ispython = flags.get('python')
194
195 lockflag = flags.get('lockfiles')
196 if lockflag:
197 lockfiles = [f for f in d.expand(lockflag).split()]
198 else:
199 lockfiles = None
200
201 tempdir = d.getVar('T', True)
202
203 # or func allows items to be executed outside of the normal
204 # task set, such as buildhistory
205 task = d.getVar('BB_RUNTASK', True) or func
206 if task == func:
207 taskfunc = task
208 else:
209 taskfunc = "%s.%s" % (task, func)
210
211 runfmt = d.getVar('BB_RUNFMT', True) or "run.{func}.{pid}"
212 runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
213 runfile = os.path.join(tempdir, runfn)
214 bb.utils.mkdirhier(os.path.dirname(runfile))
215
216 # Setup the courtesy link to the runfn, only for tasks
217 # we create the link 'just' before the run script is created
218 # if we create it after, and if the run script fails, then the
219 # link won't be created as an exception would be fired.
220 if task == func:
221 runlink = os.path.join(tempdir, 'run.{0}'.format(task))
222 if runlink:
223 bb.utils.remove(runlink)
224
225 try:
226 os.symlink(runfn, runlink)
227 except OSError:
228 pass
229
230 with bb.utils.fileslocked(lockfiles):
231 if ispython:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500232 exec_func_python(func, d, runfile, cwd=adir, pythonexception=pythonexception)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500233 else:
234 exec_func_shell(func, d, runfile, cwd=adir)
235
236_functionfmt = """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500237{function}(d)
238"""
239logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500240def exec_func_python(func, d, runfile, cwd=None, pythonexception=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500241 """Execute a python BB 'function'"""
242
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500243 code = _functionfmt.format(function=func)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500244 bb.utils.mkdirhier(os.path.dirname(runfile))
245 with open(runfile, 'w') as script:
246 bb.data.emit_func_python(func, script, d)
247
248 if cwd:
249 try:
250 olddir = os.getcwd()
251 except OSError:
252 olddir = None
253 os.chdir(cwd)
254
255 bb.debug(2, "Executing python function %s" % func)
256
257 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500258 text = "def %s(d):\n%s" % (func, d.getVar(func, False))
259 fn = d.getVarFlag(func, "filename", False)
260 lineno = int(d.getVarFlag(func, "lineno", False))
261 bb.methodpool.insert_method(func, text, fn, lineno - 1)
262
263 comp = utils.better_compile(code, func, "exec_python_func() autogenerated")
264 utils.better_exec(comp, {"d": d}, code, "exec_python_func() autogenerated", pythonexception=pythonexception)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500265 except (bb.parse.SkipRecipe, bb.build.FuncFailed):
266 raise
267 except:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500268 if pythonexception:
269 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500270 raise FuncFailed(func, None)
271 finally:
272 bb.debug(2, "Python function %s finished" % func)
273
274 if cwd and olddir:
275 try:
276 os.chdir(olddir)
277 except OSError:
278 pass
279
280def shell_trap_code():
281 return '''#!/bin/sh\n
282# Emit a useful diagnostic if something fails:
283bb_exit_handler() {
284 ret=$?
285 case $ret in
286 0) ;;
287 *) case $BASH_VERSION in
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500288 "") echo "WARNING: exit code $ret from a shell command.";;
289 *) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from '$BASH_COMMAND'";;
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500290 esac
291 exit $ret
292 esac
293}
294trap 'bb_exit_handler' 0
295set -e
296'''
297
298def exec_func_shell(func, d, runfile, cwd=None):
299 """Execute a shell function from the metadata
300
301 Note on directory behavior. The 'dirs' varflag should contain a list
302 of the directories you need created prior to execution. The last
303 item in the list is where we will chdir/cd to.
304 """
305
306 # Don't let the emitted shell script override PWD
307 d.delVarFlag('PWD', 'export')
308
309 with open(runfile, 'w') as script:
310 script.write(shell_trap_code())
311
312 bb.data.emit_func(func, script, d)
313
314 if bb.msg.loggerVerboseLogs:
315 script.write("set -x\n")
316 if cwd:
317 script.write("cd '%s'\n" % cwd)
318 script.write("%s\n" % func)
319 script.write('''
320# cleanup
321ret=$?
322trap '' 0
323exit $ret
324''')
325
326 os.chmod(runfile, 0775)
327
328 cmd = runfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500329 if d.getVarFlag(func, 'fakeroot', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500330 fakerootcmd = d.getVar('FAKEROOT', True)
331 if fakerootcmd:
332 cmd = [fakerootcmd, runfile]
333
334 if bb.msg.loggerDefaultVerbose:
335 logfile = LogTee(logger, sys.stdout)
336 else:
337 logfile = sys.stdout
338
339 def readfifo(data):
340 lines = data.split('\0')
341 for line in lines:
342 splitval = line.split(' ', 1)
343 cmd = splitval[0]
344 if len(splitval) > 1:
345 value = splitval[1]
346 else:
347 value = ''
348 if cmd == 'bbplain':
349 bb.plain(value)
350 elif cmd == 'bbnote':
351 bb.note(value)
352 elif cmd == 'bbwarn':
353 bb.warn(value)
354 elif cmd == 'bberror':
355 bb.error(value)
356 elif cmd == 'bbfatal':
357 # The caller will call exit themselves, so bb.error() is
358 # what we want here rather than bb.fatal()
359 bb.error(value)
360 elif cmd == 'bbfatal_log':
361 bb.error(value, forcelog=True)
362 elif cmd == 'bbdebug':
363 splitval = value.split(' ', 1)
364 level = int(splitval[0])
365 value = splitval[1]
366 bb.debug(level, value)
367
368 tempdir = d.getVar('T', True)
369 fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
370 if os.path.exists(fifopath):
371 os.unlink(fifopath)
372 os.mkfifo(fifopath)
373 with open(fifopath, 'r+') as fifo:
374 try:
375 bb.debug(2, "Executing shell function %s" % func)
376
377 try:
378 with open(os.devnull, 'r+') as stdin:
379 bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
380 except bb.process.CmdError:
381 logfn = d.getVar('BB_LOGFILE', True)
382 raise FuncFailed(func, logfn)
383 finally:
384 os.unlink(fifopath)
385
386 bb.debug(2, "Shell function %s finished" % func)
387
388def _task_data(fn, task, d):
389 localdata = bb.data.createCopy(d)
390 localdata.setVar('BB_FILENAME', fn)
391 localdata.setVar('BB_CURRENTTASK', task[3:])
392 localdata.setVar('OVERRIDES', 'task-%s:%s' %
393 (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
394 localdata.finalize()
395 bb.data.expandKeys(localdata)
396 return localdata
397
398def _exec_task(fn, task, d, quieterr):
399 """Execute a BB 'task'
400
401 Execution of a task involves a bit more setup than executing a function,
402 running it with its own local metadata, and with some useful variables set.
403 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500404 if not d.getVarFlag(task, 'task', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500405 event.fire(TaskInvalid(task, d), d)
406 logger.error("No such task: %s" % task)
407 return 1
408
409 logger.debug(1, "Executing task %s", task)
410
411 localdata = _task_data(fn, task, d)
412 tempdir = localdata.getVar('T', True)
413 if not tempdir:
414 bb.fatal("T variable not set, unable to build")
415
416 # Change nice level if we're asked to
417 nice = localdata.getVar("BB_TASK_NICE_LEVEL", True)
418 if nice:
419 curnice = os.nice(0)
420 nice = int(nice) - curnice
421 newnice = os.nice(nice)
422 logger.debug(1, "Renice to %s " % newnice)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500423 ionice = localdata.getVar("BB_TASK_IONICE_LEVEL", True)
424 if ionice:
425 try:
426 cls, prio = ionice.split(".", 1)
427 bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
428 except:
429 bb.warn("Invalid ionice level %s" % ionice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500430
431 bb.utils.mkdirhier(tempdir)
432
433 # Determine the logfile to generate
434 logfmt = localdata.getVar('BB_LOGFMT', True) or 'log.{task}.{pid}'
435 logbase = logfmt.format(task=task, pid=os.getpid())
436
437 # Document the order of the tasks...
438 logorder = os.path.join(tempdir, 'log.task_order')
439 try:
440 with open(logorder, 'a') as logorderfile:
441 logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
442 except OSError:
443 logger.exception("Opening log file '%s'", logorder)
444 pass
445
446 # Setup the courtesy link to the logfn
447 loglink = os.path.join(tempdir, 'log.{0}'.format(task))
448 logfn = os.path.join(tempdir, logbase)
449 if loglink:
450 bb.utils.remove(loglink)
451
452 try:
453 os.symlink(logbase, loglink)
454 except OSError:
455 pass
456
457 prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
458 postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
459
460 class ErrorCheckHandler(logging.Handler):
461 def __init__(self):
462 self.triggered = False
463 logging.Handler.__init__(self, logging.ERROR)
464 def emit(self, record):
465 if getattr(record, 'forcelog', False):
466 self.triggered = False
467 else:
468 self.triggered = True
469
470 # Handle logfiles
471 si = open('/dev/null', 'r')
472 try:
473 bb.utils.mkdirhier(os.path.dirname(logfn))
474 logfile = open(logfn, 'w')
475 except OSError:
476 logger.exception("Opening log file '%s'", logfn)
477 pass
478
479 # Dup the existing fds so we dont lose them
480 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
481 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
482 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
483
484 # Replace those fds with our own
485 os.dup2(si.fileno(), osi[1])
486 os.dup2(logfile.fileno(), oso[1])
487 os.dup2(logfile.fileno(), ose[1])
488
489 # Ensure Python logging goes to the logfile
490 handler = logging.StreamHandler(logfile)
491 handler.setFormatter(logformatter)
492 # Always enable full debug output into task logfiles
493 handler.setLevel(logging.DEBUG - 2)
494 bblogger.addHandler(handler)
495
496 errchk = ErrorCheckHandler()
497 bblogger.addHandler(errchk)
498
499 localdata.setVar('BB_LOGFILE', logfn)
500 localdata.setVar('BB_RUNTASK', task)
501
502 flags = localdata.getVarFlags(task)
503
504 event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
505 try:
506 for func in (prefuncs or '').split():
507 exec_func(func, localdata)
508 exec_func(task, localdata)
509 for func in (postfuncs or '').split():
510 exec_func(func, localdata)
511 except FuncFailed as exc:
512 if quieterr:
513 event.fire(TaskFailedSilent(task, logfn, localdata), localdata)
514 else:
515 errprinted = errchk.triggered
516 logger.error(str(exc))
517 event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata)
518 return 1
519 finally:
520 sys.stdout.flush()
521 sys.stderr.flush()
522
523 bblogger.removeHandler(handler)
524
525 # Restore the backup fds
526 os.dup2(osi[0], osi[1])
527 os.dup2(oso[0], oso[1])
528 os.dup2(ose[0], ose[1])
529
530 # Close the backup fds
531 os.close(osi[0])
532 os.close(oso[0])
533 os.close(ose[0])
534 si.close()
535
536 logfile.close()
537 if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
538 logger.debug(2, "Zero size logfn %s, removing", logfn)
539 bb.utils.remove(logfn)
540 bb.utils.remove(loglink)
541 event.fire(TaskSucceeded(task, logfn, localdata), localdata)
542
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500543 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500544 make_stamp(task, localdata)
545
546 return 0
547
548def exec_task(fn, task, d, profile = False):
549 try:
550 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500551 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500552 quieterr = True
553
554 if profile:
555 profname = "profile-%s.log" % (d.getVar("PN", True) + "-" + task)
556 try:
557 import cProfile as profile
558 except:
559 import profile
560 prof = profile.Profile()
561 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
562 prof.dump_stats(profname)
563 bb.utils.process_profilelog(profname)
564
565 return ret
566 else:
567 return _exec_task(fn, task, d, quieterr)
568
569 except Exception:
570 from traceback import format_exc
571 if not quieterr:
572 logger.error("Build of %s failed" % (task))
573 logger.error(format_exc())
574 failedevent = TaskFailed(task, None, d, True)
575 event.fire(failedevent, d)
576 return 1
577
578def stamp_internal(taskname, d, file_name, baseonly=False):
579 """
580 Internal stamp helper function
581 Makes sure the stamp directory exists
582 Returns the stamp path+filename
583
584 In the bitbake core, d can be a CacheData and file_name will be set.
585 When called in task context, d will be a data store, file_name will not be set
586 """
587 taskflagname = taskname
588 if taskname.endswith("_setscene") and taskname != "do_setscene":
589 taskflagname = taskname.replace("_setscene", "")
590
591 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500592 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500593 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
594 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500595 stamp = d.getVar('STAMP', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500596 file_name = d.getVar('BB_FILENAME', True)
597 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
598
599 if baseonly:
600 return stamp
601
602 if not stamp:
603 return
604
605 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
606
607 stampdir = os.path.dirname(stamp)
608 if cached_mtime_noerror(stampdir) == 0:
609 bb.utils.mkdirhier(stampdir)
610
611 return stamp
612
613def stamp_cleanmask_internal(taskname, d, file_name):
614 """
615 Internal stamp helper function to generate stamp cleaning mask
616 Returns the stamp path+filename
617
618 In the bitbake core, d can be a CacheData and file_name will be set.
619 When called in task context, d will be a data store, file_name will not be set
620 """
621 taskflagname = taskname
622 if taskname.endswith("_setscene") and taskname != "do_setscene":
623 taskflagname = taskname.replace("_setscene", "")
624
625 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500626 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
628 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500629 stamp = d.getVar('STAMPCLEAN', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500630 file_name = d.getVar('BB_FILENAME', True)
631 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
632
633 if not stamp:
634 return []
635
636 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
637
638 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
639
640def make_stamp(task, d, file_name = None):
641 """
642 Creates/updates a stamp for a given task
643 (d can be a data dict or dataCache)
644 """
645 cleanmask = stamp_cleanmask_internal(task, d, file_name)
646 for mask in cleanmask:
647 for name in glob.glob(mask):
648 # Preserve sigdata files in the stamps directory
649 if "sigdata" in name:
650 continue
651 # Preserve taint files in the stamps directory
652 if name.endswith('.taint'):
653 continue
654 os.unlink(name)
655
656 stamp = stamp_internal(task, d, file_name)
657 # Remove the file and recreate to force timestamp
658 # change on broken NFS filesystems
659 if stamp:
660 bb.utils.remove(stamp)
661 open(stamp, "w").close()
662
663 # If we're in task context, write out a signature file for each task
664 # as it completes
665 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
666 stampbase = stamp_internal(task, d, None, True)
667 file_name = d.getVar('BB_FILENAME', True)
668 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
669
670def del_stamp(task, d, file_name = None):
671 """
672 Removes a stamp for a given task
673 (d can be a data dict or dataCache)
674 """
675 stamp = stamp_internal(task, d, file_name)
676 bb.utils.remove(stamp)
677
678def write_taint(task, d, file_name = None):
679 """
680 Creates a "taint" file which will force the specified task and its
681 dependents to be re-run the next time by influencing the value of its
682 taskhash.
683 (d can be a data dict or dataCache)
684 """
685 import uuid
686 if file_name:
687 taintfn = d.stamp[file_name] + '.' + task + '.taint'
688 else:
689 taintfn = d.getVar('STAMP', True) + '.' + task + '.taint'
690 bb.utils.mkdirhier(os.path.dirname(taintfn))
691 # The specific content of the taint file is not really important,
692 # we just need it to be random, so a random UUID is used
693 with open(taintfn, 'w') as taintf:
694 taintf.write(str(uuid.uuid4()))
695
696def stampfile(taskname, d, file_name = None):
697 """
698 Return the stamp for a given task
699 (d can be a data dict or dataCache)
700 """
701 return stamp_internal(taskname, d, file_name)
702
703def add_tasks(tasklist, d):
704 task_deps = d.getVar('_task_deps', False)
705 if not task_deps:
706 task_deps = {}
707 if not 'tasks' in task_deps:
708 task_deps['tasks'] = []
709 if not 'parents' in task_deps:
710 task_deps['parents'] = {}
711
712 for task in tasklist:
713 task = d.expand(task)
714
715 d.setVarFlag(task, 'task', 1)
716
717 if not task in task_deps['tasks']:
718 task_deps['tasks'].append(task)
719
720 flags = d.getVarFlags(task)
721 def getTask(name):
722 if not name in task_deps:
723 task_deps[name] = {}
724 if name in flags:
725 deptask = d.expand(flags[name])
726 task_deps[name][task] = deptask
727 getTask('depends')
728 getTask('rdepends')
729 getTask('deptask')
730 getTask('rdeptask')
731 getTask('recrdeptask')
732 getTask('recideptask')
733 getTask('nostamp')
734 getTask('fakeroot')
735 getTask('noexec')
736 getTask('umask')
737 task_deps['parents'][task] = []
738 if 'deps' in flags:
739 for dep in flags['deps']:
740 dep = d.expand(dep)
741 task_deps['parents'][task].append(dep)
742
743 # don't assume holding a reference
744 d.setVar('_task_deps', task_deps)
745
746def addtask(task, before, after, d):
747 if task[:3] != "do_":
748 task = "do_" + task
749
750 d.setVarFlag(task, "task", 1)
751 bbtasks = d.getVar('__BBTASKS', False) or []
752 if task not in bbtasks:
753 bbtasks.append(task)
754 d.setVar('__BBTASKS', bbtasks)
755
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500756 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500757 if after is not None:
758 # set up deps for function
759 for entry in after.split():
760 if entry not in existing:
761 existing.append(entry)
762 d.setVarFlag(task, "deps", existing)
763 if before is not None:
764 # set up things that depend on this func
765 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500766 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500767 if task not in existing:
768 d.setVarFlag(entry, "deps", [task] + existing)
769
770def deltask(task, d):
771 if task[:3] != "do_":
772 task = "do_" + task
773
774 bbtasks = d.getVar('__BBTASKS', False) or []
775 if task in bbtasks:
776 bbtasks.remove(task)
777 d.setVar('__BBTASKS', bbtasks)
778
779 d.delVarFlag(task, 'deps')
780 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500781 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500782 if task in deps:
783 deps.remove(task)
784 d.setVarFlag(bbtask, 'deps', deps)