blob: c4c8aeb645ae141ae79f84c7865ec83a8143cf69 [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
Patrick Williamsc0f7c042017-02-23 20:41:17 -060038import bb.progress
39from bb import data, event, utils
Patrick Williamsc124f4f2015-09-15 14:41:29 -050040
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.
Patrick Williamsc0f7c042017-02-23 20:41:17 -060064if hasattr(__builtins__, '__setitem__'):
65 builtins = __builtins__
66else:
67 builtins = __builtins__.__dict__
68
69builtins['bb'] = bb
70builtins['os'] = os
Patrick Williamsc124f4f2015-09-15 14:41:29 -050071
72class FuncFailed(Exception):
73 def __init__(self, name = None, logfile = None):
74 self.logfile = logfile
75 self.name = name
76 if name:
77 self.msg = 'Function failed: %s' % name
78 else:
79 self.msg = "Function failed"
80
81 def __str__(self):
82 if self.logfile and os.path.exists(self.logfile):
83 msg = ("%s (log file is located at %s)" %
84 (self.msg, self.logfile))
85 else:
86 msg = self.msg
87 return msg
88
89class TaskBase(event.Event):
90 """Base class for task events"""
91
92 def __init__(self, t, logfile, d):
93 self._task = t
94 self._package = d.getVar("PF", True)
95 self.taskfile = d.getVar("FILE", True)
96 self.taskname = self._task
97 self.logfile = logfile
98 self.time = time.time()
99 event.Event.__init__(self)
100 self._message = "recipe %s: task %s: %s" % (d.getVar("PF", True), t, self.getDisplayName())
101
102 def getTask(self):
103 return self._task
104
105 def setTask(self, task):
106 self._task = task
107
108 def getDisplayName(self):
109 return bb.event.getName(self)[4:]
110
111 task = property(getTask, setTask, None, "task property")
112
113class TaskStarted(TaskBase):
114 """Task execution started"""
115 def __init__(self, t, logfile, taskflags, d):
116 super(TaskStarted, self).__init__(t, logfile, d)
117 self.taskflags = taskflags
118
119class TaskSucceeded(TaskBase):
120 """Task execution completed"""
121
122class TaskFailed(TaskBase):
123 """Task execution failed"""
124
125 def __init__(self, task, logfile, metadata, errprinted = False):
126 self.errprinted = errprinted
127 super(TaskFailed, self).__init__(task, logfile, metadata)
128
129class TaskFailedSilent(TaskBase):
130 """Task execution failed (silently)"""
131 def getDisplayName(self):
132 # Don't need to tell the user it was silent
133 return "Failed"
134
135class TaskInvalid(TaskBase):
136
137 def __init__(self, task, metadata):
138 super(TaskInvalid, self).__init__(task, None, metadata)
139 self._message = "No such task '%s'" % task
140
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600141class TaskProgress(event.Event):
142 """
143 Task made some progress that could be reported to the user, usually in
144 the form of a progress bar or similar.
145 NOTE: this class does not inherit from TaskBase since it doesn't need
146 to - it's fired within the task context itself, so we don't have any of
147 the context information that you do in the case of the other events.
148 The event PID can be used to determine which task it came from.
149 The progress value is normally 0-100, but can also be negative
150 indicating that progress has been made but we aren't able to determine
151 how much.
152 The rate is optional, this is simply an extra string to display to the
153 user if specified.
154 """
155 def __init__(self, progress, rate=None):
156 self.progress = progress
157 self.rate = rate
158 event.Event.__init__(self)
159
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500160
161class LogTee(object):
162 def __init__(self, logger, outfile):
163 self.outfile = outfile
164 self.logger = logger
165 self.name = self.outfile.name
166
167 def write(self, string):
168 self.logger.plain(string)
169 self.outfile.write(string)
170
171 def __enter__(self):
172 self.outfile.__enter__()
173 return self
174
175 def __exit__(self, *excinfo):
176 self.outfile.__exit__(*excinfo)
177
178 def __repr__(self):
179 return '<LogTee {0}>'.format(self.name)
180 def flush(self):
181 self.outfile.flush()
182
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500183#
184# pythonexception allows the python exceptions generated to be raised
185# as the real exceptions (not FuncFailed) and without a backtrace at the
186# origin of the failure.
187#
188def exec_func(func, d, dirs = None, pythonexception=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500189 """Execute a BB 'function'"""
190
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600191 try:
192 oldcwd = os.getcwd()
193 except:
194 oldcwd = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195
196 flags = d.getVarFlags(func)
197 cleandirs = flags.get('cleandirs')
198 if cleandirs:
199 for cdir in d.expand(cleandirs).split():
200 bb.utils.remove(cdir, True)
201 bb.utils.mkdirhier(cdir)
202
203 if dirs is None:
204 dirs = flags.get('dirs')
205 if dirs:
206 dirs = d.expand(dirs).split()
207
208 if dirs:
209 for adir in dirs:
210 bb.utils.mkdirhier(adir)
211 adir = dirs[-1]
212 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600213 adir = None
214
215 body = d.getVar(func, False)
216 if not body:
217 if body is None:
218 logger.warning("Function %s doesn't exist", func)
219 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500220
221 ispython = flags.get('python')
222
223 lockflag = flags.get('lockfiles')
224 if lockflag:
225 lockfiles = [f for f in d.expand(lockflag).split()]
226 else:
227 lockfiles = None
228
229 tempdir = d.getVar('T', True)
230
231 # or func allows items to be executed outside of the normal
232 # task set, such as buildhistory
233 task = d.getVar('BB_RUNTASK', True) or func
234 if task == func:
235 taskfunc = task
236 else:
237 taskfunc = "%s.%s" % (task, func)
238
239 runfmt = d.getVar('BB_RUNFMT', True) or "run.{func}.{pid}"
240 runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
241 runfile = os.path.join(tempdir, runfn)
242 bb.utils.mkdirhier(os.path.dirname(runfile))
243
244 # Setup the courtesy link to the runfn, only for tasks
245 # we create the link 'just' before the run script is created
246 # if we create it after, and if the run script fails, then the
247 # link won't be created as an exception would be fired.
248 if task == func:
249 runlink = os.path.join(tempdir, 'run.{0}'.format(task))
250 if runlink:
251 bb.utils.remove(runlink)
252
253 try:
254 os.symlink(runfn, runlink)
255 except OSError:
256 pass
257
258 with bb.utils.fileslocked(lockfiles):
259 if ispython:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500260 exec_func_python(func, d, runfile, cwd=adir, pythonexception=pythonexception)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500261 else:
262 exec_func_shell(func, d, runfile, cwd=adir)
263
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600264 try:
265 curcwd = os.getcwd()
266 except:
267 curcwd = None
268
269 if oldcwd and curcwd != oldcwd:
270 try:
271 bb.warn("Task %s changed cwd to %s" % (func, curcwd))
272 os.chdir(oldcwd)
273 except:
274 pass
275
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500276_functionfmt = """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500277{function}(d)
278"""
279logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500280def exec_func_python(func, d, runfile, cwd=None, pythonexception=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500281 """Execute a python BB 'function'"""
282
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500283 code = _functionfmt.format(function=func)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500284 bb.utils.mkdirhier(os.path.dirname(runfile))
285 with open(runfile, 'w') as script:
286 bb.data.emit_func_python(func, script, d)
287
288 if cwd:
289 try:
290 olddir = os.getcwd()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600291 except OSError as e:
292 bb.warn("%s: Cannot get cwd: %s" % (func, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500293 olddir = None
294 os.chdir(cwd)
295
296 bb.debug(2, "Executing python function %s" % func)
297
298 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500299 text = "def %s(d):\n%s" % (func, d.getVar(func, False))
300 fn = d.getVarFlag(func, "filename", False)
301 lineno = int(d.getVarFlag(func, "lineno", False))
302 bb.methodpool.insert_method(func, text, fn, lineno - 1)
303
304 comp = utils.better_compile(code, func, "exec_python_func() autogenerated")
305 utils.better_exec(comp, {"d": d}, code, "exec_python_func() autogenerated", pythonexception=pythonexception)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500306 except (bb.parse.SkipRecipe, bb.build.FuncFailed):
307 raise
308 except:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500309 if pythonexception:
310 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311 raise FuncFailed(func, None)
312 finally:
313 bb.debug(2, "Python function %s finished" % func)
314
315 if cwd and olddir:
316 try:
317 os.chdir(olddir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600318 except OSError as e:
319 bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500320
321def shell_trap_code():
322 return '''#!/bin/sh\n
323# Emit a useful diagnostic if something fails:
324bb_exit_handler() {
325 ret=$?
326 case $ret in
327 0) ;;
328 *) case $BASH_VERSION in
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500329 "") echo "WARNING: exit code $ret from a shell command.";;
330 *) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from '$BASH_COMMAND'";;
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500331 esac
332 exit $ret
333 esac
334}
335trap 'bb_exit_handler' 0
336set -e
337'''
338
339def exec_func_shell(func, d, runfile, cwd=None):
340 """Execute a shell function from the metadata
341
342 Note on directory behavior. The 'dirs' varflag should contain a list
343 of the directories you need created prior to execution. The last
344 item in the list is where we will chdir/cd to.
345 """
346
347 # Don't let the emitted shell script override PWD
348 d.delVarFlag('PWD', 'export')
349
350 with open(runfile, 'w') as script:
351 script.write(shell_trap_code())
352
353 bb.data.emit_func(func, script, d)
354
355 if bb.msg.loggerVerboseLogs:
356 script.write("set -x\n")
357 if cwd:
358 script.write("cd '%s'\n" % cwd)
359 script.write("%s\n" % func)
360 script.write('''
361# cleanup
362ret=$?
363trap '' 0
364exit $ret
365''')
366
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600367 os.chmod(runfile, 0o775)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500368
369 cmd = runfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500370 if d.getVarFlag(func, 'fakeroot', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500371 fakerootcmd = d.getVar('FAKEROOT', True)
372 if fakerootcmd:
373 cmd = [fakerootcmd, runfile]
374
375 if bb.msg.loggerDefaultVerbose:
376 logfile = LogTee(logger, sys.stdout)
377 else:
378 logfile = sys.stdout
379
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600380 progress = d.getVarFlag(func, 'progress', True)
381 if progress:
382 if progress == 'percent':
383 # Use default regex
384 logfile = bb.progress.BasicProgressHandler(d, outfile=logfile)
385 elif progress.startswith('percent:'):
386 # Use specified regex
387 logfile = bb.progress.BasicProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
388 elif progress.startswith('outof:'):
389 # Use specified regex
390 logfile = bb.progress.OutOfProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
391 else:
392 bb.warn('%s: invalid task progress varflag value "%s", ignoring' % (func, progress))
393
394 fifobuffer = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500395 def readfifo(data):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600396 nonlocal fifobuffer
397 fifobuffer.extend(data)
398 while fifobuffer:
399 message, token, nextmsg = fifobuffer.partition(b"\00")
400 if token:
401 splitval = message.split(b' ', 1)
402 cmd = splitval[0].decode("utf-8")
403 if len(splitval) > 1:
404 value = splitval[1].decode("utf-8")
405 else:
406 value = ''
407 if cmd == 'bbplain':
408 bb.plain(value)
409 elif cmd == 'bbnote':
410 bb.note(value)
411 elif cmd == 'bbwarn':
412 bb.warn(value)
413 elif cmd == 'bberror':
414 bb.error(value)
415 elif cmd == 'bbfatal':
416 # The caller will call exit themselves, so bb.error() is
417 # what we want here rather than bb.fatal()
418 bb.error(value)
419 elif cmd == 'bbfatal_log':
420 bb.error(value, forcelog=True)
421 elif cmd == 'bbdebug':
422 splitval = value.split(' ', 1)
423 level = int(splitval[0])
424 value = splitval[1]
425 bb.debug(level, value)
426 else:
427 bb.warn("Unrecognised command '%s' on FIFO" % cmd)
428 fifobuffer = nextmsg
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500429 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600430 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500431
432 tempdir = d.getVar('T', True)
433 fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
434 if os.path.exists(fifopath):
435 os.unlink(fifopath)
436 os.mkfifo(fifopath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600437 with open(fifopath, 'r+b', buffering=0) as fifo:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500438 try:
439 bb.debug(2, "Executing shell function %s" % func)
440
441 try:
442 with open(os.devnull, 'r+') as stdin:
443 bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
444 except bb.process.CmdError:
445 logfn = d.getVar('BB_LOGFILE', True)
446 raise FuncFailed(func, logfn)
447 finally:
448 os.unlink(fifopath)
449
450 bb.debug(2, "Shell function %s finished" % func)
451
452def _task_data(fn, task, d):
453 localdata = bb.data.createCopy(d)
454 localdata.setVar('BB_FILENAME', fn)
455 localdata.setVar('BB_CURRENTTASK', task[3:])
456 localdata.setVar('OVERRIDES', 'task-%s:%s' %
457 (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
458 localdata.finalize()
459 bb.data.expandKeys(localdata)
460 return localdata
461
462def _exec_task(fn, task, d, quieterr):
463 """Execute a BB 'task'
464
465 Execution of a task involves a bit more setup than executing a function,
466 running it with its own local metadata, and with some useful variables set.
467 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500468 if not d.getVarFlag(task, 'task', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500469 event.fire(TaskInvalid(task, d), d)
470 logger.error("No such task: %s" % task)
471 return 1
472
473 logger.debug(1, "Executing task %s", task)
474
475 localdata = _task_data(fn, task, d)
476 tempdir = localdata.getVar('T', True)
477 if not tempdir:
478 bb.fatal("T variable not set, unable to build")
479
480 # Change nice level if we're asked to
481 nice = localdata.getVar("BB_TASK_NICE_LEVEL", True)
482 if nice:
483 curnice = os.nice(0)
484 nice = int(nice) - curnice
485 newnice = os.nice(nice)
486 logger.debug(1, "Renice to %s " % newnice)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500487 ionice = localdata.getVar("BB_TASK_IONICE_LEVEL", True)
488 if ionice:
489 try:
490 cls, prio = ionice.split(".", 1)
491 bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
492 except:
493 bb.warn("Invalid ionice level %s" % ionice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500494
495 bb.utils.mkdirhier(tempdir)
496
497 # Determine the logfile to generate
498 logfmt = localdata.getVar('BB_LOGFMT', True) or 'log.{task}.{pid}'
499 logbase = logfmt.format(task=task, pid=os.getpid())
500
501 # Document the order of the tasks...
502 logorder = os.path.join(tempdir, 'log.task_order')
503 try:
504 with open(logorder, 'a') as logorderfile:
505 logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
506 except OSError:
507 logger.exception("Opening log file '%s'", logorder)
508 pass
509
510 # Setup the courtesy link to the logfn
511 loglink = os.path.join(tempdir, 'log.{0}'.format(task))
512 logfn = os.path.join(tempdir, logbase)
513 if loglink:
514 bb.utils.remove(loglink)
515
516 try:
517 os.symlink(logbase, loglink)
518 except OSError:
519 pass
520
521 prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
522 postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
523
524 class ErrorCheckHandler(logging.Handler):
525 def __init__(self):
526 self.triggered = False
527 logging.Handler.__init__(self, logging.ERROR)
528 def emit(self, record):
529 if getattr(record, 'forcelog', False):
530 self.triggered = False
531 else:
532 self.triggered = True
533
534 # Handle logfiles
535 si = open('/dev/null', 'r')
536 try:
537 bb.utils.mkdirhier(os.path.dirname(logfn))
538 logfile = open(logfn, 'w')
539 except OSError:
540 logger.exception("Opening log file '%s'", logfn)
541 pass
542
543 # Dup the existing fds so we dont lose them
544 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
545 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
546 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
547
548 # Replace those fds with our own
549 os.dup2(si.fileno(), osi[1])
550 os.dup2(logfile.fileno(), oso[1])
551 os.dup2(logfile.fileno(), ose[1])
552
553 # Ensure Python logging goes to the logfile
554 handler = logging.StreamHandler(logfile)
555 handler.setFormatter(logformatter)
556 # Always enable full debug output into task logfiles
557 handler.setLevel(logging.DEBUG - 2)
558 bblogger.addHandler(handler)
559
560 errchk = ErrorCheckHandler()
561 bblogger.addHandler(errchk)
562
563 localdata.setVar('BB_LOGFILE', logfn)
564 localdata.setVar('BB_RUNTASK', task)
565
566 flags = localdata.getVarFlags(task)
567
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500568 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600569 try:
570 event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
571 except (bb.BBHandledException, SystemExit):
572 return 1
573 except FuncFailed as exc:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500574 logger.error(str(exc))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600575 return 1
576
577 try:
578 for func in (prefuncs or '').split():
579 exec_func(func, localdata)
580 exec_func(task, localdata)
581 for func in (postfuncs or '').split():
582 exec_func(func, localdata)
583 except FuncFailed as exc:
584 if quieterr:
585 event.fire(TaskFailedSilent(task, logfn, localdata), localdata)
586 else:
587 errprinted = errchk.triggered
588 logger.error(str(exc))
589 event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata)
590 return 1
591 except bb.BBHandledException:
592 event.fire(TaskFailed(task, logfn, localdata, True), localdata)
593 return 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500594 finally:
595 sys.stdout.flush()
596 sys.stderr.flush()
597
598 bblogger.removeHandler(handler)
599
600 # Restore the backup fds
601 os.dup2(osi[0], osi[1])
602 os.dup2(oso[0], oso[1])
603 os.dup2(ose[0], ose[1])
604
605 # Close the backup fds
606 os.close(osi[0])
607 os.close(oso[0])
608 os.close(ose[0])
609 si.close()
610
611 logfile.close()
612 if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
613 logger.debug(2, "Zero size logfn %s, removing", logfn)
614 bb.utils.remove(logfn)
615 bb.utils.remove(loglink)
616 event.fire(TaskSucceeded(task, logfn, localdata), localdata)
617
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500618 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500619 make_stamp(task, localdata)
620
621 return 0
622
623def exec_task(fn, task, d, profile = False):
624 try:
625 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500626 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627 quieterr = True
628
629 if profile:
630 profname = "profile-%s.log" % (d.getVar("PN", True) + "-" + task)
631 try:
632 import cProfile as profile
633 except:
634 import profile
635 prof = profile.Profile()
636 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
637 prof.dump_stats(profname)
638 bb.utils.process_profilelog(profname)
639
640 return ret
641 else:
642 return _exec_task(fn, task, d, quieterr)
643
644 except Exception:
645 from traceback import format_exc
646 if not quieterr:
647 logger.error("Build of %s failed" % (task))
648 logger.error(format_exc())
649 failedevent = TaskFailed(task, None, d, True)
650 event.fire(failedevent, d)
651 return 1
652
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600653def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654 """
655 Internal stamp helper function
656 Makes sure the stamp directory exists
657 Returns the stamp path+filename
658
659 In the bitbake core, d can be a CacheData and file_name will be set.
660 When called in task context, d will be a data store, file_name will not be set
661 """
662 taskflagname = taskname
663 if taskname.endswith("_setscene") and taskname != "do_setscene":
664 taskflagname = taskname.replace("_setscene", "")
665
666 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500667 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500668 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
669 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500670 stamp = d.getVar('STAMP', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500671 file_name = d.getVar('BB_FILENAME', True)
672 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
673
674 if baseonly:
675 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600676 if noextra:
677 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500678
679 if not stamp:
680 return
681
682 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
683
684 stampdir = os.path.dirname(stamp)
685 if cached_mtime_noerror(stampdir) == 0:
686 bb.utils.mkdirhier(stampdir)
687
688 return stamp
689
690def stamp_cleanmask_internal(taskname, d, file_name):
691 """
692 Internal stamp helper function to generate stamp cleaning mask
693 Returns the stamp path+filename
694
695 In the bitbake core, d can be a CacheData and file_name will be set.
696 When called in task context, d will be a data store, file_name will not be set
697 """
698 taskflagname = taskname
699 if taskname.endswith("_setscene") and taskname != "do_setscene":
700 taskflagname = taskname.replace("_setscene", "")
701
702 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500703 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500704 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
705 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500706 stamp = d.getVar('STAMPCLEAN', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707 file_name = d.getVar('BB_FILENAME', True)
708 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
709
710 if not stamp:
711 return []
712
713 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
714
715 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
716
717def make_stamp(task, d, file_name = None):
718 """
719 Creates/updates a stamp for a given task
720 (d can be a data dict or dataCache)
721 """
722 cleanmask = stamp_cleanmask_internal(task, d, file_name)
723 for mask in cleanmask:
724 for name in glob.glob(mask):
725 # Preserve sigdata files in the stamps directory
726 if "sigdata" in name:
727 continue
728 # Preserve taint files in the stamps directory
729 if name.endswith('.taint'):
730 continue
731 os.unlink(name)
732
733 stamp = stamp_internal(task, d, file_name)
734 # Remove the file and recreate to force timestamp
735 # change on broken NFS filesystems
736 if stamp:
737 bb.utils.remove(stamp)
738 open(stamp, "w").close()
739
740 # If we're in task context, write out a signature file for each task
741 # as it completes
742 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
743 stampbase = stamp_internal(task, d, None, True)
744 file_name = d.getVar('BB_FILENAME', True)
745 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
746
747def del_stamp(task, d, file_name = None):
748 """
749 Removes a stamp for a given task
750 (d can be a data dict or dataCache)
751 """
752 stamp = stamp_internal(task, d, file_name)
753 bb.utils.remove(stamp)
754
755def write_taint(task, d, file_name = None):
756 """
757 Creates a "taint" file which will force the specified task and its
758 dependents to be re-run the next time by influencing the value of its
759 taskhash.
760 (d can be a data dict or dataCache)
761 """
762 import uuid
763 if file_name:
764 taintfn = d.stamp[file_name] + '.' + task + '.taint'
765 else:
766 taintfn = d.getVar('STAMP', True) + '.' + task + '.taint'
767 bb.utils.mkdirhier(os.path.dirname(taintfn))
768 # The specific content of the taint file is not really important,
769 # we just need it to be random, so a random UUID is used
770 with open(taintfn, 'w') as taintf:
771 taintf.write(str(uuid.uuid4()))
772
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600773def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774 """
775 Return the stamp for a given task
776 (d can be a data dict or dataCache)
777 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600778 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779
780def add_tasks(tasklist, d):
781 task_deps = d.getVar('_task_deps', False)
782 if not task_deps:
783 task_deps = {}
784 if not 'tasks' in task_deps:
785 task_deps['tasks'] = []
786 if not 'parents' in task_deps:
787 task_deps['parents'] = {}
788
789 for task in tasklist:
790 task = d.expand(task)
791
792 d.setVarFlag(task, 'task', 1)
793
794 if not task in task_deps['tasks']:
795 task_deps['tasks'].append(task)
796
797 flags = d.getVarFlags(task)
798 def getTask(name):
799 if not name in task_deps:
800 task_deps[name] = {}
801 if name in flags:
802 deptask = d.expand(flags[name])
803 task_deps[name][task] = deptask
804 getTask('depends')
805 getTask('rdepends')
806 getTask('deptask')
807 getTask('rdeptask')
808 getTask('recrdeptask')
809 getTask('recideptask')
810 getTask('nostamp')
811 getTask('fakeroot')
812 getTask('noexec')
813 getTask('umask')
814 task_deps['parents'][task] = []
815 if 'deps' in flags:
816 for dep in flags['deps']:
817 dep = d.expand(dep)
818 task_deps['parents'][task].append(dep)
819
820 # don't assume holding a reference
821 d.setVar('_task_deps', task_deps)
822
823def addtask(task, before, after, d):
824 if task[:3] != "do_":
825 task = "do_" + task
826
827 d.setVarFlag(task, "task", 1)
828 bbtasks = d.getVar('__BBTASKS', False) or []
829 if task not in bbtasks:
830 bbtasks.append(task)
831 d.setVar('__BBTASKS', bbtasks)
832
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500833 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834 if after is not None:
835 # set up deps for function
836 for entry in after.split():
837 if entry not in existing:
838 existing.append(entry)
839 d.setVarFlag(task, "deps", existing)
840 if before is not None:
841 # set up things that depend on this func
842 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500843 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844 if task not in existing:
845 d.setVarFlag(entry, "deps", [task] + existing)
846
847def deltask(task, d):
848 if task[:3] != "do_":
849 task = "do_" + task
850
851 bbtasks = d.getVar('__BBTASKS', False) or []
852 if task in bbtasks:
853 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600854 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500855 d.setVar('__BBTASKS', bbtasks)
856
857 d.delVarFlag(task, 'deps')
858 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500859 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500860 if task in deps:
861 deps.remove(task)
862 d.setVarFlag(bbtask, 'deps', deps)