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