blob: dae42ac47d8012e2ebaa60c27f3cdbc013b20dff [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#
2# BitBake 'Build' implementation
3#
4# Core code for function execution and task handling in the
5# BitBake build tools.
6#
7# Copyright (C) 2003, 2004 Chris Larson
8#
9# Based on Gentoo's portage.py.
10#
Brad Bishopc342db32019-05-15 21:57:59 -040011# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050012#
13# Based on functions from the base bb module, Copyright 2003 Holger Schurig
14
15import os
16import sys
17import logging
18import shlex
19import glob
20import time
21import stat
22import bb
23import bb.msg
24import bb.process
Patrick Williamsc0f7c042017-02-23 20:41:17 -060025import bb.progress
26from bb import data, event, utils
Patrick Williamsc124f4f2015-09-15 14:41:29 -050027
28bblogger = logging.getLogger('BitBake')
29logger = logging.getLogger('BitBake.Build')
30
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031__mtime_cache = {}
32
33def cached_mtime_noerror(f):
34 if f not in __mtime_cache:
35 try:
36 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
37 except OSError:
38 return 0
39 return __mtime_cache[f]
40
41def reset_cache():
42 global __mtime_cache
43 __mtime_cache = {}
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.
Patrick Williamsc0f7c042017-02-23 20:41:17 -060049if hasattr(__builtins__, '__setitem__'):
50 builtins = __builtins__
51else:
52 builtins = __builtins__.__dict__
53
54builtins['bb'] = bb
55builtins['os'] = os
Patrick Williamsc124f4f2015-09-15 14:41:29 -050056
57class FuncFailed(Exception):
58 def __init__(self, name = None, logfile = None):
59 self.logfile = logfile
60 self.name = name
61 if name:
62 self.msg = 'Function failed: %s' % name
63 else:
64 self.msg = "Function failed"
65
66 def __str__(self):
67 if self.logfile and os.path.exists(self.logfile):
68 msg = ("%s (log file is located at %s)" %
69 (self.msg, self.logfile))
70 else:
71 msg = self.msg
72 return msg
73
74class TaskBase(event.Event):
75 """Base class for task events"""
76
77 def __init__(self, t, logfile, d):
78 self._task = t
Brad Bishop6e60e8b2018-02-01 10:27:11 -050079 self._package = d.getVar("PF")
80 self._mc = d.getVar("BB_CURRENT_MC")
81 self.taskfile = d.getVar("FILE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050082 self.taskname = self._task
83 self.logfile = logfile
84 self.time = time.time()
85 event.Event.__init__(self)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050086 self._message = "recipe %s: task %s: %s" % (d.getVar("PF"), t, self.getDisplayName())
Patrick Williamsc124f4f2015-09-15 14:41:29 -050087
88 def getTask(self):
89 return self._task
90
91 def setTask(self, task):
92 self._task = task
93
94 def getDisplayName(self):
95 return bb.event.getName(self)[4:]
96
97 task = property(getTask, setTask, None, "task property")
98
99class TaskStarted(TaskBase):
100 """Task execution started"""
101 def __init__(self, t, logfile, taskflags, d):
102 super(TaskStarted, self).__init__(t, logfile, d)
103 self.taskflags = taskflags
104
105class TaskSucceeded(TaskBase):
106 """Task execution completed"""
107
108class TaskFailed(TaskBase):
109 """Task execution failed"""
110
111 def __init__(self, task, logfile, metadata, errprinted = False):
112 self.errprinted = errprinted
113 super(TaskFailed, self).__init__(task, logfile, metadata)
114
115class TaskFailedSilent(TaskBase):
116 """Task execution failed (silently)"""
117 def getDisplayName(self):
118 # Don't need to tell the user it was silent
119 return "Failed"
120
121class TaskInvalid(TaskBase):
122
123 def __init__(self, task, metadata):
124 super(TaskInvalid, self).__init__(task, None, metadata)
125 self._message = "No such task '%s'" % task
126
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600127class TaskProgress(event.Event):
128 """
129 Task made some progress that could be reported to the user, usually in
130 the form of a progress bar or similar.
131 NOTE: this class does not inherit from TaskBase since it doesn't need
132 to - it's fired within the task context itself, so we don't have any of
133 the context information that you do in the case of the other events.
134 The event PID can be used to determine which task it came from.
135 The progress value is normally 0-100, but can also be negative
136 indicating that progress has been made but we aren't able to determine
137 how much.
138 The rate is optional, this is simply an extra string to display to the
139 user if specified.
140 """
141 def __init__(self, progress, rate=None):
142 self.progress = progress
143 self.rate = rate
144 event.Event.__init__(self)
145
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500146
147class LogTee(object):
148 def __init__(self, logger, outfile):
149 self.outfile = outfile
150 self.logger = logger
151 self.name = self.outfile.name
152
153 def write(self, string):
154 self.logger.plain(string)
155 self.outfile.write(string)
156
157 def __enter__(self):
158 self.outfile.__enter__()
159 return self
160
161 def __exit__(self, *excinfo):
162 self.outfile.__exit__(*excinfo)
163
164 def __repr__(self):
165 return '<LogTee {0}>'.format(self.name)
166 def flush(self):
167 self.outfile.flush()
168
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500169#
170# pythonexception allows the python exceptions generated to be raised
171# as the real exceptions (not FuncFailed) and without a backtrace at the
172# origin of the failure.
173#
174def exec_func(func, d, dirs = None, pythonexception=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500175 """Execute a BB 'function'"""
176
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600177 try:
178 oldcwd = os.getcwd()
179 except:
180 oldcwd = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500181
182 flags = d.getVarFlags(func)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500183 cleandirs = flags.get('cleandirs') if flags else None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500184 if cleandirs:
185 for cdir in d.expand(cleandirs).split():
186 bb.utils.remove(cdir, True)
187 bb.utils.mkdirhier(cdir)
188
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500189 if flags and dirs is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500190 dirs = flags.get('dirs')
191 if dirs:
192 dirs = d.expand(dirs).split()
193
194 if dirs:
195 for adir in dirs:
196 bb.utils.mkdirhier(adir)
197 adir = dirs[-1]
198 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600199 adir = None
200
201 body = d.getVar(func, False)
202 if not body:
203 if body is None:
204 logger.warning("Function %s doesn't exist", func)
205 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500206
207 ispython = flags.get('python')
208
209 lockflag = flags.get('lockfiles')
210 if lockflag:
211 lockfiles = [f for f in d.expand(lockflag).split()]
212 else:
213 lockfiles = None
214
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500215 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500216
217 # or func allows items to be executed outside of the normal
218 # task set, such as buildhistory
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500219 task = d.getVar('BB_RUNTASK') or func
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500220 if task == func:
221 taskfunc = task
222 else:
223 taskfunc = "%s.%s" % (task, func)
224
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500225 runfmt = d.getVar('BB_RUNFMT') or "run.{func}.{pid}"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500226 runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
227 runfile = os.path.join(tempdir, runfn)
228 bb.utils.mkdirhier(os.path.dirname(runfile))
229
230 # Setup the courtesy link to the runfn, only for tasks
231 # we create the link 'just' before the run script is created
232 # if we create it after, and if the run script fails, then the
233 # link won't be created as an exception would be fired.
234 if task == func:
235 runlink = os.path.join(tempdir, 'run.{0}'.format(task))
236 if runlink:
237 bb.utils.remove(runlink)
238
239 try:
240 os.symlink(runfn, runlink)
241 except OSError:
242 pass
243
244 with bb.utils.fileslocked(lockfiles):
245 if ispython:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500246 exec_func_python(func, d, runfile, cwd=adir, pythonexception=pythonexception)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500247 else:
248 exec_func_shell(func, d, runfile, cwd=adir)
249
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600250 try:
251 curcwd = os.getcwd()
252 except:
253 curcwd = None
254
255 if oldcwd and curcwd != oldcwd:
256 try:
257 bb.warn("Task %s changed cwd to %s" % (func, curcwd))
258 os.chdir(oldcwd)
259 except:
260 pass
261
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262_functionfmt = """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500263{function}(d)
264"""
265logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500266def exec_func_python(func, d, runfile, cwd=None, pythonexception=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500267 """Execute a python BB 'function'"""
268
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500269 code = _functionfmt.format(function=func)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500270 bb.utils.mkdirhier(os.path.dirname(runfile))
271 with open(runfile, 'w') as script:
272 bb.data.emit_func_python(func, script, d)
273
274 if cwd:
275 try:
276 olddir = os.getcwd()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600277 except OSError as e:
278 bb.warn("%s: Cannot get cwd: %s" % (func, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500279 olddir = None
280 os.chdir(cwd)
281
282 bb.debug(2, "Executing python function %s" % func)
283
284 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500285 text = "def %s(d):\n%s" % (func, d.getVar(func, False))
286 fn = d.getVarFlag(func, "filename", False)
287 lineno = int(d.getVarFlag(func, "lineno", False))
288 bb.methodpool.insert_method(func, text, fn, lineno - 1)
289
290 comp = utils.better_compile(code, func, "exec_python_func() autogenerated")
291 utils.better_exec(comp, {"d": d}, code, "exec_python_func() autogenerated", pythonexception=pythonexception)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500292 except (bb.parse.SkipRecipe, bb.build.FuncFailed):
293 raise
Brad Bishop19323692019-04-05 15:28:33 -0400294 except Exception as e:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500295 if pythonexception:
296 raise
Brad Bishop19323692019-04-05 15:28:33 -0400297 logger.error(str(e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500298 raise FuncFailed(func, None)
299 finally:
300 bb.debug(2, "Python function %s finished" % func)
301
302 if cwd and olddir:
303 try:
304 os.chdir(olddir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600305 except OSError as e:
306 bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500307
308def shell_trap_code():
309 return '''#!/bin/sh\n
310# Emit a useful diagnostic if something fails:
311bb_exit_handler() {
312 ret=$?
313 case $ret in
314 0) ;;
315 *) case $BASH_VERSION in
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500316 "") echo "WARNING: exit code $ret from a shell command.";;
317 *) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from '$BASH_COMMAND'";;
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500318 esac
319 exit $ret
320 esac
321}
322trap 'bb_exit_handler' 0
323set -e
324'''
325
326def exec_func_shell(func, d, runfile, cwd=None):
327 """Execute a shell function from the metadata
328
329 Note on directory behavior. The 'dirs' varflag should contain a list
330 of the directories you need created prior to execution. The last
331 item in the list is where we will chdir/cd to.
332 """
333
334 # Don't let the emitted shell script override PWD
335 d.delVarFlag('PWD', 'export')
336
337 with open(runfile, 'w') as script:
338 script.write(shell_trap_code())
339
340 bb.data.emit_func(func, script, d)
341
342 if bb.msg.loggerVerboseLogs:
343 script.write("set -x\n")
344 if cwd:
345 script.write("cd '%s'\n" % cwd)
346 script.write("%s\n" % func)
347 script.write('''
348# cleanup
349ret=$?
350trap '' 0
351exit $ret
352''')
353
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600354 os.chmod(runfile, 0o775)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500355
356 cmd = runfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500357 if d.getVarFlag(func, 'fakeroot', False):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500358 fakerootcmd = d.getVar('FAKEROOT')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500359 if fakerootcmd:
360 cmd = [fakerootcmd, runfile]
361
362 if bb.msg.loggerDefaultVerbose:
363 logfile = LogTee(logger, sys.stdout)
364 else:
365 logfile = sys.stdout
366
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500367 progress = d.getVarFlag(func, 'progress')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600368 if progress:
369 if progress == 'percent':
370 # Use default regex
371 logfile = bb.progress.BasicProgressHandler(d, outfile=logfile)
372 elif progress.startswith('percent:'):
373 # Use specified regex
374 logfile = bb.progress.BasicProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
375 elif progress.startswith('outof:'):
376 # Use specified regex
377 logfile = bb.progress.OutOfProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
378 else:
379 bb.warn('%s: invalid task progress varflag value "%s", ignoring' % (func, progress))
380
381 fifobuffer = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500382 def readfifo(data):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600383 nonlocal fifobuffer
384 fifobuffer.extend(data)
385 while fifobuffer:
386 message, token, nextmsg = fifobuffer.partition(b"\00")
387 if token:
388 splitval = message.split(b' ', 1)
389 cmd = splitval[0].decode("utf-8")
390 if len(splitval) > 1:
391 value = splitval[1].decode("utf-8")
392 else:
393 value = ''
394 if cmd == 'bbplain':
395 bb.plain(value)
396 elif cmd == 'bbnote':
397 bb.note(value)
Brad Bishopc342db32019-05-15 21:57:59 -0400398 elif cmd == 'bbverbnote':
399 bb.verbnote(value)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600400 elif cmd == 'bbwarn':
401 bb.warn(value)
402 elif cmd == 'bberror':
403 bb.error(value)
404 elif cmd == 'bbfatal':
405 # The caller will call exit themselves, so bb.error() is
406 # what we want here rather than bb.fatal()
407 bb.error(value)
408 elif cmd == 'bbfatal_log':
409 bb.error(value, forcelog=True)
410 elif cmd == 'bbdebug':
411 splitval = value.split(' ', 1)
412 level = int(splitval[0])
413 value = splitval[1]
414 bb.debug(level, value)
415 else:
416 bb.warn("Unrecognised command '%s' on FIFO" % cmd)
417 fifobuffer = nextmsg
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500418 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600419 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500421 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500422 fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
423 if os.path.exists(fifopath):
424 os.unlink(fifopath)
425 os.mkfifo(fifopath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600426 with open(fifopath, 'r+b', buffering=0) as fifo:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500427 try:
428 bb.debug(2, "Executing shell function %s" % func)
429
430 try:
431 with open(os.devnull, 'r+') as stdin:
432 bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
433 except bb.process.CmdError:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500434 logfn = d.getVar('BB_LOGFILE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500435 raise FuncFailed(func, logfn)
436 finally:
437 os.unlink(fifopath)
438
439 bb.debug(2, "Shell function %s finished" % func)
440
441def _task_data(fn, task, d):
442 localdata = bb.data.createCopy(d)
443 localdata.setVar('BB_FILENAME', fn)
444 localdata.setVar('BB_CURRENTTASK', task[3:])
445 localdata.setVar('OVERRIDES', 'task-%s:%s' %
446 (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
447 localdata.finalize()
448 bb.data.expandKeys(localdata)
449 return localdata
450
451def _exec_task(fn, task, d, quieterr):
452 """Execute a BB 'task'
453
454 Execution of a task involves a bit more setup than executing a function,
455 running it with its own local metadata, and with some useful variables set.
456 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500457 if not d.getVarFlag(task, 'task', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500458 event.fire(TaskInvalid(task, d), d)
459 logger.error("No such task: %s" % task)
460 return 1
461
462 logger.debug(1, "Executing task %s", task)
463
464 localdata = _task_data(fn, task, d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500465 tempdir = localdata.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500466 if not tempdir:
467 bb.fatal("T variable not set, unable to build")
468
469 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500470 nice = localdata.getVar("BB_TASK_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471 if nice:
472 curnice = os.nice(0)
473 nice = int(nice) - curnice
474 newnice = os.nice(nice)
475 logger.debug(1, "Renice to %s " % newnice)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500476 ionice = localdata.getVar("BB_TASK_IONICE_LEVEL")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500477 if ionice:
478 try:
479 cls, prio = ionice.split(".", 1)
480 bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
481 except:
482 bb.warn("Invalid ionice level %s" % ionice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500483
484 bb.utils.mkdirhier(tempdir)
485
486 # Determine the logfile to generate
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500487 logfmt = localdata.getVar('BB_LOGFMT') or 'log.{task}.{pid}'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488 logbase = logfmt.format(task=task, pid=os.getpid())
489
490 # Document the order of the tasks...
491 logorder = os.path.join(tempdir, 'log.task_order')
492 try:
493 with open(logorder, 'a') as logorderfile:
494 logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
495 except OSError:
496 logger.exception("Opening log file '%s'", logorder)
497 pass
498
499 # Setup the courtesy link to the logfn
500 loglink = os.path.join(tempdir, 'log.{0}'.format(task))
501 logfn = os.path.join(tempdir, logbase)
502 if loglink:
503 bb.utils.remove(loglink)
504
505 try:
506 os.symlink(logbase, loglink)
507 except OSError:
508 pass
509
510 prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
511 postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
512
513 class ErrorCheckHandler(logging.Handler):
514 def __init__(self):
515 self.triggered = False
516 logging.Handler.__init__(self, logging.ERROR)
517 def emit(self, record):
518 if getattr(record, 'forcelog', False):
519 self.triggered = False
520 else:
521 self.triggered = True
522
523 # Handle logfiles
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500524 try:
525 bb.utils.mkdirhier(os.path.dirname(logfn))
526 logfile = open(logfn, 'w')
527 except OSError:
528 logger.exception("Opening log file '%s'", logfn)
529 pass
530
531 # Dup the existing fds so we dont lose them
532 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
533 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
534 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
535
536 # Replace those fds with our own
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800537 with open('/dev/null', 'r') as si:
538 os.dup2(si.fileno(), osi[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500539 os.dup2(logfile.fileno(), oso[1])
540 os.dup2(logfile.fileno(), ose[1])
541
542 # Ensure Python logging goes to the logfile
543 handler = logging.StreamHandler(logfile)
544 handler.setFormatter(logformatter)
545 # Always enable full debug output into task logfiles
546 handler.setLevel(logging.DEBUG - 2)
547 bblogger.addHandler(handler)
548
549 errchk = ErrorCheckHandler()
550 bblogger.addHandler(errchk)
551
552 localdata.setVar('BB_LOGFILE', logfn)
553 localdata.setVar('BB_RUNTASK', task)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500554 localdata.setVar('BB_TASK_LOGGER', bblogger)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500555
556 flags = localdata.getVarFlags(task)
557
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500558 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600559 try:
560 event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
561 except (bb.BBHandledException, SystemExit):
562 return 1
563 except FuncFailed as exc:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500564 logger.error(str(exc))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600565 return 1
566
567 try:
568 for func in (prefuncs or '').split():
569 exec_func(func, localdata)
570 exec_func(task, localdata)
571 for func in (postfuncs or '').split():
572 exec_func(func, localdata)
573 except FuncFailed as exc:
574 if quieterr:
575 event.fire(TaskFailedSilent(task, logfn, localdata), localdata)
576 else:
577 errprinted = errchk.triggered
578 logger.error(str(exc))
579 event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata)
580 return 1
581 except bb.BBHandledException:
582 event.fire(TaskFailed(task, logfn, localdata, True), localdata)
583 return 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500584 finally:
585 sys.stdout.flush()
586 sys.stderr.flush()
587
588 bblogger.removeHandler(handler)
589
590 # Restore the backup fds
591 os.dup2(osi[0], osi[1])
592 os.dup2(oso[0], oso[1])
593 os.dup2(ose[0], ose[1])
594
595 # Close the backup fds
596 os.close(osi[0])
597 os.close(oso[0])
598 os.close(ose[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500599
600 logfile.close()
601 if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
602 logger.debug(2, "Zero size logfn %s, removing", logfn)
603 bb.utils.remove(logfn)
604 bb.utils.remove(loglink)
605 event.fire(TaskSucceeded(task, logfn, localdata), localdata)
606
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500607 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500608 make_stamp(task, localdata)
609
610 return 0
611
612def exec_task(fn, task, d, profile = False):
613 try:
614 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500615 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616 quieterr = True
617
618 if profile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500619 profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500620 try:
621 import cProfile as profile
622 except:
623 import profile
624 prof = profile.Profile()
625 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
626 prof.dump_stats(profname)
627 bb.utils.process_profilelog(profname)
628
629 return ret
630 else:
631 return _exec_task(fn, task, d, quieterr)
632
633 except Exception:
634 from traceback import format_exc
635 if not quieterr:
636 logger.error("Build of %s failed" % (task))
637 logger.error(format_exc())
638 failedevent = TaskFailed(task, None, d, True)
639 event.fire(failedevent, d)
640 return 1
641
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600642def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500643 """
644 Internal stamp helper function
645 Makes sure the stamp directory exists
646 Returns the stamp path+filename
647
648 In the bitbake core, d can be a CacheData and file_name will be set.
649 When called in task context, d will be a data store, file_name will not be set
650 """
651 taskflagname = taskname
652 if taskname.endswith("_setscene") and taskname != "do_setscene":
653 taskflagname = taskname.replace("_setscene", "")
654
655 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500656 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500657 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
658 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500659 stamp = d.getVar('STAMP')
660 file_name = d.getVar('BB_FILENAME')
661 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500662
663 if baseonly:
664 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600665 if noextra:
666 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500667
668 if not stamp:
669 return
670
671 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
672
673 stampdir = os.path.dirname(stamp)
674 if cached_mtime_noerror(stampdir) == 0:
675 bb.utils.mkdirhier(stampdir)
676
677 return stamp
678
679def stamp_cleanmask_internal(taskname, d, file_name):
680 """
681 Internal stamp helper function to generate stamp cleaning mask
682 Returns the stamp path+filename
683
684 In the bitbake core, d can be a CacheData and file_name will be set.
685 When called in task context, d will be a data store, file_name will not be set
686 """
687 taskflagname = taskname
688 if taskname.endswith("_setscene") and taskname != "do_setscene":
689 taskflagname = taskname.replace("_setscene", "")
690
691 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500692 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500693 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
694 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500695 stamp = d.getVar('STAMPCLEAN')
696 file_name = d.getVar('BB_FILENAME')
697 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500698
699 if not stamp:
700 return []
701
702 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
703
704 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
705
706def make_stamp(task, d, file_name = None):
707 """
708 Creates/updates a stamp for a given task
709 (d can be a data dict or dataCache)
710 """
711 cleanmask = stamp_cleanmask_internal(task, d, file_name)
712 for mask in cleanmask:
713 for name in glob.glob(mask):
714 # Preserve sigdata files in the stamps directory
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500715 if "sigdata" in name or "sigbasedata" in name:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500716 continue
717 # Preserve taint files in the stamps directory
718 if name.endswith('.taint'):
719 continue
720 os.unlink(name)
721
722 stamp = stamp_internal(task, d, file_name)
723 # Remove the file and recreate to force timestamp
724 # change on broken NFS filesystems
725 if stamp:
726 bb.utils.remove(stamp)
727 open(stamp, "w").close()
728
729 # If we're in task context, write out a signature file for each task
730 # as it completes
731 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
732 stampbase = stamp_internal(task, d, None, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500733 file_name = d.getVar('BB_FILENAME')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500734 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
735
736def del_stamp(task, d, file_name = None):
737 """
738 Removes a stamp for a given task
739 (d can be a data dict or dataCache)
740 """
741 stamp = stamp_internal(task, d, file_name)
742 bb.utils.remove(stamp)
743
744def write_taint(task, d, file_name = None):
745 """
746 Creates a "taint" file which will force the specified task and its
747 dependents to be re-run the next time by influencing the value of its
748 taskhash.
749 (d can be a data dict or dataCache)
750 """
751 import uuid
752 if file_name:
753 taintfn = d.stamp[file_name] + '.' + task + '.taint'
754 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500755 taintfn = d.getVar('STAMP') + '.' + task + '.taint'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756 bb.utils.mkdirhier(os.path.dirname(taintfn))
757 # The specific content of the taint file is not really important,
758 # we just need it to be random, so a random UUID is used
759 with open(taintfn, 'w') as taintf:
760 taintf.write(str(uuid.uuid4()))
761
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600762def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500763 """
764 Return the stamp for a given task
765 (d can be a data dict or dataCache)
766 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600767 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500768
769def add_tasks(tasklist, d):
770 task_deps = d.getVar('_task_deps', False)
771 if not task_deps:
772 task_deps = {}
773 if not 'tasks' in task_deps:
774 task_deps['tasks'] = []
775 if not 'parents' in task_deps:
776 task_deps['parents'] = {}
777
778 for task in tasklist:
779 task = d.expand(task)
780
781 d.setVarFlag(task, 'task', 1)
782
783 if not task in task_deps['tasks']:
784 task_deps['tasks'].append(task)
785
786 flags = d.getVarFlags(task)
787 def getTask(name):
788 if not name in task_deps:
789 task_deps[name] = {}
790 if name in flags:
791 deptask = d.expand(flags[name])
792 task_deps[name][task] = deptask
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800793 getTask('mcdepends')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500794 getTask('depends')
795 getTask('rdepends')
796 getTask('deptask')
797 getTask('rdeptask')
798 getTask('recrdeptask')
799 getTask('recideptask')
800 getTask('nostamp')
801 getTask('fakeroot')
802 getTask('noexec')
803 getTask('umask')
804 task_deps['parents'][task] = []
805 if 'deps' in flags:
806 for dep in flags['deps']:
Brad Bishopc342db32019-05-15 21:57:59 -0400807 # Check and warn for "addtask task after foo" while foo does not exist
808 #if not dep in tasklist:
809 # bb.warn('%s: dependent task %s for %s does not exist' % (d.getVar('PN'), dep, task))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500810 dep = d.expand(dep)
811 task_deps['parents'][task].append(dep)
812
813 # don't assume holding a reference
814 d.setVar('_task_deps', task_deps)
815
816def addtask(task, before, after, d):
817 if task[:3] != "do_":
818 task = "do_" + task
819
820 d.setVarFlag(task, "task", 1)
821 bbtasks = d.getVar('__BBTASKS', False) or []
822 if task not in bbtasks:
823 bbtasks.append(task)
824 d.setVar('__BBTASKS', bbtasks)
825
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500826 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500827 if after is not None:
828 # set up deps for function
829 for entry in after.split():
830 if entry not in existing:
831 existing.append(entry)
832 d.setVarFlag(task, "deps", existing)
833 if before is not None:
834 # set up things that depend on this func
835 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500836 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500837 if task not in existing:
838 d.setVarFlag(entry, "deps", [task] + existing)
839
840def deltask(task, d):
841 if task[:3] != "do_":
842 task = "do_" + task
843
844 bbtasks = d.getVar('__BBTASKS', False) or []
845 if task in bbtasks:
846 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600847 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500848 d.setVar('__BBTASKS', bbtasks)
849
850 d.delVarFlag(task, 'deps')
851 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500852 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500853 if task in deps:
854 deps.remove(task)
855 d.setVarFlag(bbtask, 'deps', deps)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500856
857def preceedtask(task, with_recrdeptasks, d):
858 """
859 Returns a set of tasks in the current recipe which were specified as
860 precondition by the task itself ("after") or which listed themselves
861 as precondition ("before"). Preceeding tasks specified via the
862 "recrdeptask" are included in the result only if requested. Beware
863 that this may lead to the task itself being listed.
864 """
865 preceed = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400866
867 # Ignore tasks which don't exist
868 tasks = d.getVar('__BBTASKS', False)
869 if task not in tasks:
870 return preceed
871
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500872 preceed.update(d.getVarFlag(task, 'deps') or [])
873 if with_recrdeptasks:
874 recrdeptask = d.getVarFlag(task, 'recrdeptask')
875 if recrdeptask:
876 preceed.update(recrdeptask.split())
877 return preceed
878
879def tasksbetween(task_start, task_end, d):
880 """
881 Return the list of tasks between two tasks in the current recipe,
882 where task_start is to start at and task_end is the task to end at
883 (and task_end has a dependency chain back to task_start).
884 """
885 outtasks = []
886 tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
887 def follow_chain(task, endtask, chain=None):
888 if not chain:
889 chain = []
890 chain.append(task)
891 for othertask in tasks:
892 if othertask == task:
893 continue
894 if task == endtask:
895 for ctask in chain:
896 if ctask not in outtasks:
897 outtasks.append(ctask)
898 else:
899 deps = d.getVarFlag(othertask, 'deps', False)
900 if task in deps:
901 follow_chain(othertask, endtask, chain)
902 chain.pop()
903 follow_chain(task_start, task_end)
904 return outtasks