blob: 94f9cb371c3940c467b78c7f75c2932ef6948e21 [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
Patrick Williamsc124f4f2015-09-15 14:41:29 -050018import glob
Andrew Geissler635e0e42020-08-21 15:58:33 -050019import itertools
Patrick Williamsc124f4f2015-09-15 14:41:29 -050020import time
Andrew Geissler635e0e42020-08-21 15:58:33 -050021import re
Patrick Williamsc124f4f2015-09-15 14:41:29 -050022import stat
23import bb
24import bb.msg
25import bb.process
Patrick Williamsc0f7c042017-02-23 20:41:17 -060026import bb.progress
27from bb import data, event, utils
Patrick Williamsc124f4f2015-09-15 14:41:29 -050028
29bblogger = logging.getLogger('BitBake')
30logger = logging.getLogger('BitBake.Build')
31
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032__mtime_cache = {}
33
34def cached_mtime_noerror(f):
35 if f not in __mtime_cache:
36 try:
37 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
38 except OSError:
39 return 0
40 return __mtime_cache[f]
41
42def reset_cache():
43 global __mtime_cache
44 __mtime_cache = {}
45
46# When we execute a Python function, we'd like certain things
47# in all namespaces, hence we add them to __builtins__.
48# If we do not do this and use the exec globals, they will
49# not be available to subfunctions.
Patrick Williamsc0f7c042017-02-23 20:41:17 -060050if hasattr(__builtins__, '__setitem__'):
51 builtins = __builtins__
52else:
53 builtins = __builtins__.__dict__
54
55builtins['bb'] = bb
56builtins['os'] = os
Patrick Williamsc124f4f2015-09-15 14:41:29 -050057
Patrick Williamsc124f4f2015-09-15 14:41:29 -050058class TaskBase(event.Event):
59 """Base class for task events"""
60
Andrew Geissler82c905d2020-04-13 13:39:40 -050061 def __init__(self, t, fn, logfile, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050062 self._task = t
Andrew Geissler82c905d2020-04-13 13:39:40 -050063 self._fn = fn
Brad Bishop6e60e8b2018-02-01 10:27:11 -050064 self._package = d.getVar("PF")
65 self._mc = d.getVar("BB_CURRENT_MC")
66 self.taskfile = d.getVar("FILE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050067 self.taskname = self._task
68 self.logfile = logfile
69 self.time = time.time()
Andrew Geissler82c905d2020-04-13 13:39:40 -050070 self.pn = d.getVar("PN")
71 self.pv = d.getVar("PV")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050072 event.Event.__init__(self)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050073 self._message = "recipe %s: task %s: %s" % (d.getVar("PF"), t, self.getDisplayName())
Patrick Williamsc124f4f2015-09-15 14:41:29 -050074
75 def getTask(self):
76 return self._task
77
78 def setTask(self, task):
79 self._task = task
80
81 def getDisplayName(self):
82 return bb.event.getName(self)[4:]
83
84 task = property(getTask, setTask, None, "task property")
85
86class TaskStarted(TaskBase):
87 """Task execution started"""
Andrew Geissler82c905d2020-04-13 13:39:40 -050088 def __init__(self, t, fn, logfile, taskflags, d):
89 super(TaskStarted, self).__init__(t, fn, logfile, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090 self.taskflags = taskflags
91
92class TaskSucceeded(TaskBase):
93 """Task execution completed"""
94
95class TaskFailed(TaskBase):
96 """Task execution failed"""
97
Andrew Geissler82c905d2020-04-13 13:39:40 -050098 def __init__(self, task, fn, logfile, metadata, errprinted = False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050099 self.errprinted = errprinted
Andrew Geissler82c905d2020-04-13 13:39:40 -0500100 super(TaskFailed, self).__init__(task, fn, logfile, metadata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500101
102class TaskFailedSilent(TaskBase):
103 """Task execution failed (silently)"""
104 def getDisplayName(self):
105 # Don't need to tell the user it was silent
106 return "Failed"
107
108class TaskInvalid(TaskBase):
109
Andrew Geissler82c905d2020-04-13 13:39:40 -0500110 def __init__(self, task, fn, metadata):
111 super(TaskInvalid, self).__init__(task, fn, None, metadata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500112 self._message = "No such task '%s'" % task
113
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600114class TaskProgress(event.Event):
115 """
116 Task made some progress that could be reported to the user, usually in
117 the form of a progress bar or similar.
118 NOTE: this class does not inherit from TaskBase since it doesn't need
119 to - it's fired within the task context itself, so we don't have any of
120 the context information that you do in the case of the other events.
121 The event PID can be used to determine which task it came from.
122 The progress value is normally 0-100, but can also be negative
123 indicating that progress has been made but we aren't able to determine
124 how much.
125 The rate is optional, this is simply an extra string to display to the
126 user if specified.
127 """
128 def __init__(self, progress, rate=None):
129 self.progress = progress
130 self.rate = rate
131 event.Event.__init__(self)
132
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500133
134class LogTee(object):
135 def __init__(self, logger, outfile):
136 self.outfile = outfile
137 self.logger = logger
138 self.name = self.outfile.name
139
140 def write(self, string):
141 self.logger.plain(string)
142 self.outfile.write(string)
143
144 def __enter__(self):
145 self.outfile.__enter__()
146 return self
147
148 def __exit__(self, *excinfo):
149 self.outfile.__exit__(*excinfo)
150
151 def __repr__(self):
152 return '<LogTee {0}>'.format(self.name)
Brad Bishop15ae2502019-06-18 21:44:24 -0400153
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500154 def flush(self):
155 self.outfile.flush()
156
Brad Bishop15ae2502019-06-18 21:44:24 -0400157
158class StdoutNoopContextManager:
159 """
160 This class acts like sys.stdout, but adds noop __enter__ and __exit__ methods.
161 """
162 def __enter__(self):
163 return sys.stdout
164
165 def __exit__(self, *exc_info):
166 pass
167
168 def write(self, string):
169 return sys.stdout.write(string)
170
171 def flush(self):
172 sys.stdout.flush()
173
174 @property
175 def name(self):
176 return sys.stdout.name
177
178
Brad Bishop08902b02019-08-20 09:16:51 -0400179def exec_func(func, d, dirs = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500180 """Execute a BB 'function'"""
181
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600182 try:
183 oldcwd = os.getcwd()
184 except:
185 oldcwd = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500186
187 flags = d.getVarFlags(func)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500188 cleandirs = flags.get('cleandirs') if flags else None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500189 if cleandirs:
190 for cdir in d.expand(cleandirs).split():
191 bb.utils.remove(cdir, True)
192 bb.utils.mkdirhier(cdir)
193
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500194 if flags and dirs is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195 dirs = flags.get('dirs')
196 if dirs:
197 dirs = d.expand(dirs).split()
198
199 if dirs:
200 for adir in dirs:
201 bb.utils.mkdirhier(adir)
202 adir = dirs[-1]
203 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600204 adir = None
205
206 body = d.getVar(func, False)
207 if not body:
208 if body is None:
209 logger.warning("Function %s doesn't exist", func)
210 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500211
212 ispython = flags.get('python')
213
214 lockflag = flags.get('lockfiles')
215 if lockflag:
216 lockfiles = [f for f in d.expand(lockflag).split()]
217 else:
218 lockfiles = None
219
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500220 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221
222 # or func allows items to be executed outside of the normal
223 # task set, such as buildhistory
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500224 task = d.getVar('BB_RUNTASK') or func
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500225 if task == func:
226 taskfunc = task
227 else:
228 taskfunc = "%s.%s" % (task, func)
229
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500230 runfmt = d.getVar('BB_RUNFMT') or "run.{func}.{pid}"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500231 runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
232 runfile = os.path.join(tempdir, runfn)
233 bb.utils.mkdirhier(os.path.dirname(runfile))
234
235 # Setup the courtesy link to the runfn, only for tasks
236 # we create the link 'just' before the run script is created
237 # if we create it after, and if the run script fails, then the
238 # link won't be created as an exception would be fired.
239 if task == func:
240 runlink = os.path.join(tempdir, 'run.{0}'.format(task))
241 if runlink:
242 bb.utils.remove(runlink)
243
244 try:
245 os.symlink(runfn, runlink)
246 except OSError:
247 pass
248
249 with bb.utils.fileslocked(lockfiles):
250 if ispython:
Brad Bishop08902b02019-08-20 09:16:51 -0400251 exec_func_python(func, d, runfile, cwd=adir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500252 else:
253 exec_func_shell(func, d, runfile, cwd=adir)
254
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600255 try:
256 curcwd = os.getcwd()
257 except:
258 curcwd = None
259
260 if oldcwd and curcwd != oldcwd:
261 try:
262 bb.warn("Task %s changed cwd to %s" % (func, curcwd))
263 os.chdir(oldcwd)
264 except:
265 pass
266
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500267_functionfmt = """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500268{function}(d)
269"""
270logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
Brad Bishop08902b02019-08-20 09:16:51 -0400271def exec_func_python(func, d, runfile, cwd=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500272 """Execute a python BB 'function'"""
273
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500274 code = _functionfmt.format(function=func)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500275 bb.utils.mkdirhier(os.path.dirname(runfile))
276 with open(runfile, 'w') as script:
277 bb.data.emit_func_python(func, script, d)
278
279 if cwd:
280 try:
281 olddir = os.getcwd()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600282 except OSError as e:
283 bb.warn("%s: Cannot get cwd: %s" % (func, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500284 olddir = None
285 os.chdir(cwd)
286
287 bb.debug(2, "Executing python function %s" % func)
288
289 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500290 text = "def %s(d):\n%s" % (func, d.getVar(func, False))
291 fn = d.getVarFlag(func, "filename", False)
292 lineno = int(d.getVarFlag(func, "lineno", False))
293 bb.methodpool.insert_method(func, text, fn, lineno - 1)
294
295 comp = utils.better_compile(code, func, "exec_python_func() autogenerated")
Brad Bishop08902b02019-08-20 09:16:51 -0400296 utils.better_exec(comp, {"d": d}, code, "exec_python_func() autogenerated")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500297 finally:
298 bb.debug(2, "Python function %s finished" % func)
299
300 if cwd and olddir:
301 try:
302 os.chdir(olddir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600303 except OSError as e:
304 bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500305
306def shell_trap_code():
307 return '''#!/bin/sh\n
Andrew Geissler635e0e42020-08-21 15:58:33 -0500308__BITBAKE_LAST_LINE=0
309
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500310# Emit a useful diagnostic if something fails:
Andrew Geissler635e0e42020-08-21 15:58:33 -0500311bb_sh_exit_handler() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500312 ret=$?
Andrew Geissler635e0e42020-08-21 15:58:33 -0500313 if [ "$ret" != 0 ]; then
314 echo "WARNING: exit code $ret from a shell command."
315 fi
316 exit $ret
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317}
Andrew Geissler635e0e42020-08-21 15:58:33 -0500318
319bb_bash_exit_handler() {
320 ret=$?
321 { set +x; } > /dev/null
322 trap "" DEBUG
323 if [ "$ret" != 0 ]; then
324 echo "WARNING: ${BASH_SOURCE[0]}:${__BITBAKE_LAST_LINE} exit $ret from '$1'"
325
326 echo "WARNING: Backtrace (BB generated script): "
327 for i in $(seq 1 $((${#FUNCNAME[@]} - 1))); do
328 if [ "$i" -eq 1 ]; then
329 echo -e "\t#$((i)): ${FUNCNAME[$i]}, ${BASH_SOURCE[$((i-1))]}, line ${__BITBAKE_LAST_LINE}"
330 else
331 echo -e "\t#$((i)): ${FUNCNAME[$i]}, ${BASH_SOURCE[$((i-1))]}, line ${BASH_LINENO[$((i-1))]}"
332 fi
333 done
334 fi
335 exit $ret
336}
337
338bb_bash_debug_handler() {
339 local line=${BASH_LINENO[0]}
340 # For some reason the DEBUG trap trips with lineno=1 when scripts exit; ignore it
341 if [ "$line" -eq 1 ]; then
342 return
343 fi
344
345 # Track the line number of commands as they execute. This is so we can have access to the failing line number
346 # in the EXIT trap. See http://gnu-bash.2382.n7.nabble.com/trap-echo-quot-trap-exit-on-LINENO-quot-EXIT-gt-wrong-linenumber-td3666.html
347 if [ "${FUNCNAME[1]}" != "bb_bash_exit_handler" ]; then
348 __BITBAKE_LAST_LINE=$line
349 fi
350}
351
352case $BASH_VERSION in
353"") trap 'bb_sh_exit_handler' 0
354 set -e
355 ;;
356*) trap 'bb_bash_exit_handler "$BASH_COMMAND"' 0
357 trap '{ bb_bash_debug_handler; } 2>/dev/null' DEBUG
358 set -e
359 shopt -s extdebug
360 ;;
361esac
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500362'''
363
Brad Bishop15ae2502019-06-18 21:44:24 -0400364def create_progress_handler(func, progress, logfile, d):
365 if progress == 'percent':
366 # Use default regex
367 return bb.progress.BasicProgressHandler(d, outfile=logfile)
368 elif progress.startswith('percent:'):
369 # Use specified regex
370 return bb.progress.BasicProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
371 elif progress.startswith('outof:'):
372 # Use specified regex
373 return bb.progress.OutOfProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
374 elif progress.startswith("custom:"):
375 # Use a custom progress handler that was injected via OE_EXTRA_IMPORTS or __builtins__
376 import functools
377 from types import ModuleType
378
379 parts = progress.split(":", 2)
380 _, cls, otherargs = parts[0], parts[1], (parts[2] or None) if parts[2:] else None
381 if cls:
382 def resolve(x, y):
383 if not x:
384 return None
385 if isinstance(x, ModuleType):
386 return getattr(x, y, None)
387 return x.get(y)
388 cls_obj = functools.reduce(resolve, cls.split("."), bb.utils._context)
389 if not cls_obj:
390 # Fall-back on __builtins__
Andrew Geissler635e0e42020-08-21 15:58:33 -0500391 cls_obj = functools.reduce(resolve, cls.split("."), __builtins__)
Brad Bishop15ae2502019-06-18 21:44:24 -0400392 if cls_obj:
393 return cls_obj(d, outfile=logfile, otherargs=otherargs)
394 bb.warn('%s: unknown custom progress handler in task progress varflag value "%s", ignoring' % (func, cls))
395 else:
396 bb.warn('%s: invalid task progress varflag value "%s", ignoring' % (func, progress))
397
398 return logfile
399
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500400def exec_func_shell(func, d, runfile, cwd=None):
401 """Execute a shell function from the metadata
402
403 Note on directory behavior. The 'dirs' varflag should contain a list
404 of the directories you need created prior to execution. The last
405 item in the list is where we will chdir/cd to.
406 """
407
408 # Don't let the emitted shell script override PWD
409 d.delVarFlag('PWD', 'export')
410
411 with open(runfile, 'w') as script:
412 script.write(shell_trap_code())
413
414 bb.data.emit_func(func, script, d)
415
416 if bb.msg.loggerVerboseLogs:
417 script.write("set -x\n")
418 if cwd:
419 script.write("cd '%s'\n" % cwd)
420 script.write("%s\n" % func)
421 script.write('''
422# cleanup
423ret=$?
424trap '' 0
425exit $ret
426''')
427
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600428 os.chmod(runfile, 0o775)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500429
430 cmd = runfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500431 if d.getVarFlag(func, 'fakeroot', False):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500432 fakerootcmd = d.getVar('FAKEROOT')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500433 if fakerootcmd:
434 cmd = [fakerootcmd, runfile]
435
436 if bb.msg.loggerDefaultVerbose:
Brad Bishop15ae2502019-06-18 21:44:24 -0400437 logfile = LogTee(logger, StdoutNoopContextManager())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500438 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400439 logfile = StdoutNoopContextManager()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500440
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500441 progress = d.getVarFlag(func, 'progress')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600442 if progress:
Andrew Geissler635e0e42020-08-21 15:58:33 -0500443 try:
444 logfile = create_progress_handler(func, progress, logfile, d)
445 except:
446 from traceback import format_exc
447 logger.error("Failed to create progress handler")
448 logger.error(format_exc())
449 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600450
451 fifobuffer = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500452 def readfifo(data):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600453 nonlocal fifobuffer
454 fifobuffer.extend(data)
455 while fifobuffer:
456 message, token, nextmsg = fifobuffer.partition(b"\00")
457 if token:
458 splitval = message.split(b' ', 1)
459 cmd = splitval[0].decode("utf-8")
460 if len(splitval) > 1:
461 value = splitval[1].decode("utf-8")
462 else:
463 value = ''
464 if cmd == 'bbplain':
465 bb.plain(value)
466 elif cmd == 'bbnote':
467 bb.note(value)
Brad Bishopc342db32019-05-15 21:57:59 -0400468 elif cmd == 'bbverbnote':
469 bb.verbnote(value)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600470 elif cmd == 'bbwarn':
471 bb.warn(value)
472 elif cmd == 'bberror':
473 bb.error(value)
474 elif cmd == 'bbfatal':
475 # The caller will call exit themselves, so bb.error() is
476 # what we want here rather than bb.fatal()
477 bb.error(value)
478 elif cmd == 'bbfatal_log':
479 bb.error(value, forcelog=True)
480 elif cmd == 'bbdebug':
481 splitval = value.split(' ', 1)
482 level = int(splitval[0])
483 value = splitval[1]
484 bb.debug(level, value)
485 else:
486 bb.warn("Unrecognised command '%s' on FIFO" % cmd)
487 fifobuffer = nextmsg
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600489 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500490
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500491 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500492 fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
493 if os.path.exists(fifopath):
494 os.unlink(fifopath)
495 os.mkfifo(fifopath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600496 with open(fifopath, 'r+b', buffering=0) as fifo:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500497 try:
498 bb.debug(2, "Executing shell function %s" % func)
Brad Bishop08902b02019-08-20 09:16:51 -0400499 with open(os.devnull, 'r+') as stdin, logfile:
500 bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
Andrew Geissler635e0e42020-08-21 15:58:33 -0500501 except bb.process.ExecutionError as exe:
502 # Find the backtrace that the shell trap generated
503 backtrace_marker_regex = re.compile(r"WARNING: Backtrace \(BB generated script\)")
504 stdout_lines = (exe.stdout or "").split("\n")
505 backtrace_start_line = None
506 for i, line in enumerate(reversed(stdout_lines)):
507 if backtrace_marker_regex.search(line):
508 backtrace_start_line = len(stdout_lines) - i
509 break
510
511 # Read the backtrace frames, starting at the location we just found
512 backtrace_entry_regex = re.compile(r"#(?P<frameno>\d+): (?P<funcname>[^\s]+), (?P<file>.+?), line ("
513 r"?P<lineno>\d+)")
514 backtrace_frames = []
515 if backtrace_start_line:
516 for line in itertools.islice(stdout_lines, backtrace_start_line, None):
517 match = backtrace_entry_regex.search(line)
518 if match:
519 backtrace_frames.append(match.groupdict())
520
521 with open(runfile, "r") as script:
522 script_lines = [line.rstrip() for line in script.readlines()]
523
524 # For each backtrace frame, search backwards in the script (from the line number called out by the frame),
525 # to find the comment that emit_vars injected when it wrote the script. This will give us the metadata
526 # filename (e.g. .bb or .bbclass) and line number where the shell function was originally defined.
527 script_metadata_comment_regex = re.compile(r"# line: (?P<lineno>\d+), file: (?P<file>.+)")
528 better_frames = []
529 # Skip the very last frame since it's just the call to the shell task in the body of the script
530 for frame in backtrace_frames[:-1]:
531 # Check whether the frame corresponds to a function defined in the script vs external script.
532 if os.path.samefile(frame["file"], runfile):
533 # Search backwards from the frame lineno to locate the comment that BB injected
534 i = int(frame["lineno"]) - 1
535 while i >= 0:
536 match = script_metadata_comment_regex.match(script_lines[i])
537 if match:
538 # Calculate the relative line in the function itself
539 relative_line_in_function = int(frame["lineno"]) - i - 2
540 # Calculate line in the function as declared in the metadata
541 metadata_function_line = relative_line_in_function + int(match["lineno"])
542 better_frames.append("#{frameno}: {funcname}, {file}, line {lineno}".format(
543 frameno=frame["frameno"],
544 funcname=frame["funcname"],
545 file=match["file"],
546 lineno=metadata_function_line
547 ))
548 break
549 i -= 1
550 else:
551 better_frames.append("#{frameno}: {funcname}, {file}, line {lineno}".format(**frame))
552
553 if better_frames:
554 better_frames = ("\t{0}".format(frame) for frame in better_frames)
555 exe.extra_message = "\nBacktrace (metadata-relative locations):\n{0}".format("\n".join(better_frames))
556 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500557 finally:
558 os.unlink(fifopath)
559
560 bb.debug(2, "Shell function %s finished" % func)
561
562def _task_data(fn, task, d):
563 localdata = bb.data.createCopy(d)
564 localdata.setVar('BB_FILENAME', fn)
565 localdata.setVar('BB_CURRENTTASK', task[3:])
566 localdata.setVar('OVERRIDES', 'task-%s:%s' %
567 (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
568 localdata.finalize()
569 bb.data.expandKeys(localdata)
570 return localdata
571
572def _exec_task(fn, task, d, quieterr):
573 """Execute a BB 'task'
574
575 Execution of a task involves a bit more setup than executing a function,
576 running it with its own local metadata, and with some useful variables set.
577 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500578 if not d.getVarFlag(task, 'task', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500579 event.fire(TaskInvalid(task, d), d)
580 logger.error("No such task: %s" % task)
581 return 1
582
583 logger.debug(1, "Executing task %s", task)
584
585 localdata = _task_data(fn, task, d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500586 tempdir = localdata.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500587 if not tempdir:
588 bb.fatal("T variable not set, unable to build")
589
590 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500591 nice = localdata.getVar("BB_TASK_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500592 if nice:
593 curnice = os.nice(0)
594 nice = int(nice) - curnice
595 newnice = os.nice(nice)
596 logger.debug(1, "Renice to %s " % newnice)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500597 ionice = localdata.getVar("BB_TASK_IONICE_LEVEL")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500598 if ionice:
599 try:
600 cls, prio = ionice.split(".", 1)
601 bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
602 except:
603 bb.warn("Invalid ionice level %s" % ionice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500604
605 bb.utils.mkdirhier(tempdir)
606
607 # Determine the logfile to generate
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500608 logfmt = localdata.getVar('BB_LOGFMT') or 'log.{task}.{pid}'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500609 logbase = logfmt.format(task=task, pid=os.getpid())
610
611 # Document the order of the tasks...
612 logorder = os.path.join(tempdir, 'log.task_order')
613 try:
614 with open(logorder, 'a') as logorderfile:
615 logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
616 except OSError:
617 logger.exception("Opening log file '%s'", logorder)
618 pass
619
620 # Setup the courtesy link to the logfn
621 loglink = os.path.join(tempdir, 'log.{0}'.format(task))
622 logfn = os.path.join(tempdir, logbase)
623 if loglink:
624 bb.utils.remove(loglink)
625
626 try:
627 os.symlink(logbase, loglink)
628 except OSError:
629 pass
630
631 prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
632 postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
633
634 class ErrorCheckHandler(logging.Handler):
635 def __init__(self):
636 self.triggered = False
637 logging.Handler.__init__(self, logging.ERROR)
638 def emit(self, record):
639 if getattr(record, 'forcelog', False):
640 self.triggered = False
641 else:
642 self.triggered = True
643
644 # Handle logfiles
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500645 try:
646 bb.utils.mkdirhier(os.path.dirname(logfn))
647 logfile = open(logfn, 'w')
648 except OSError:
649 logger.exception("Opening log file '%s'", logfn)
650 pass
651
652 # Dup the existing fds so we dont lose them
653 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
654 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
655 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
656
657 # Replace those fds with our own
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800658 with open('/dev/null', 'r') as si:
659 os.dup2(si.fileno(), osi[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500660 os.dup2(logfile.fileno(), oso[1])
661 os.dup2(logfile.fileno(), ose[1])
662
663 # Ensure Python logging goes to the logfile
664 handler = logging.StreamHandler(logfile)
665 handler.setFormatter(logformatter)
666 # Always enable full debug output into task logfiles
667 handler.setLevel(logging.DEBUG - 2)
668 bblogger.addHandler(handler)
669
670 errchk = ErrorCheckHandler()
671 bblogger.addHandler(errchk)
672
673 localdata.setVar('BB_LOGFILE', logfn)
674 localdata.setVar('BB_RUNTASK', task)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500675 localdata.setVar('BB_TASK_LOGGER', bblogger)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500676
677 flags = localdata.getVarFlags(task)
678
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500679 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600680 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500681 event.fire(TaskStarted(task, fn, logfn, flags, localdata), localdata)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600682 except (bb.BBHandledException, SystemExit):
683 return 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600684
685 try:
686 for func in (prefuncs or '').split():
687 exec_func(func, localdata)
688 exec_func(task, localdata)
689 for func in (postfuncs or '').split():
690 exec_func(func, localdata)
Brad Bishop08902b02019-08-20 09:16:51 -0400691 except bb.BBHandledException:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500692 event.fire(TaskFailed(task, fn, logfn, localdata, True), localdata)
Brad Bishop08902b02019-08-20 09:16:51 -0400693 return 1
694 except Exception as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600695 if quieterr:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500696 event.fire(TaskFailedSilent(task, fn, logfn, localdata), localdata)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600697 else:
698 errprinted = errchk.triggered
699 logger.error(str(exc))
Andrew Geissler82c905d2020-04-13 13:39:40 -0500700 event.fire(TaskFailed(task, fn, logfn, localdata, errprinted), localdata)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600701 return 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500702 finally:
703 sys.stdout.flush()
704 sys.stderr.flush()
705
706 bblogger.removeHandler(handler)
707
708 # Restore the backup fds
709 os.dup2(osi[0], osi[1])
710 os.dup2(oso[0], oso[1])
711 os.dup2(ose[0], ose[1])
712
713 # Close the backup fds
714 os.close(osi[0])
715 os.close(oso[0])
716 os.close(ose[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500717
718 logfile.close()
719 if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
720 logger.debug(2, "Zero size logfn %s, removing", logfn)
721 bb.utils.remove(logfn)
722 bb.utils.remove(loglink)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500723 event.fire(TaskSucceeded(task, fn, logfn, localdata), localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500724
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500725 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500726 make_stamp(task, localdata)
727
728 return 0
729
730def exec_task(fn, task, d, profile = False):
731 try:
732 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500733 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500734 quieterr = True
735
736 if profile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500737 profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500738 try:
739 import cProfile as profile
740 except:
741 import profile
742 prof = profile.Profile()
743 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
744 prof.dump_stats(profname)
745 bb.utils.process_profilelog(profname)
746
747 return ret
748 else:
749 return _exec_task(fn, task, d, quieterr)
750
751 except Exception:
752 from traceback import format_exc
753 if not quieterr:
754 logger.error("Build of %s failed" % (task))
755 logger.error(format_exc())
756 failedevent = TaskFailed(task, None, d, True)
757 event.fire(failedevent, d)
758 return 1
759
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600760def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500761 """
762 Internal stamp helper function
763 Makes sure the stamp directory exists
764 Returns the stamp path+filename
765
766 In the bitbake core, d can be a CacheData and file_name will be set.
767 When called in task context, d will be a data store, file_name will not be set
768 """
769 taskflagname = taskname
770 if taskname.endswith("_setscene") and taskname != "do_setscene":
771 taskflagname = taskname.replace("_setscene", "")
772
773 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500774 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
776 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500777 stamp = d.getVar('STAMP')
778 file_name = d.getVar('BB_FILENAME')
779 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500780
781 if baseonly:
782 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600783 if noextra:
784 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500785
786 if not stamp:
787 return
788
789 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
790
791 stampdir = os.path.dirname(stamp)
792 if cached_mtime_noerror(stampdir) == 0:
793 bb.utils.mkdirhier(stampdir)
794
795 return stamp
796
797def stamp_cleanmask_internal(taskname, d, file_name):
798 """
799 Internal stamp helper function to generate stamp cleaning mask
800 Returns the stamp path+filename
801
802 In the bitbake core, d can be a CacheData and file_name will be set.
803 When called in task context, d will be a data store, file_name will not be set
804 """
805 taskflagname = taskname
806 if taskname.endswith("_setscene") and taskname != "do_setscene":
807 taskflagname = taskname.replace("_setscene", "")
808
809 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500810 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500811 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
812 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500813 stamp = d.getVar('STAMPCLEAN')
814 file_name = d.getVar('BB_FILENAME')
815 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500816
817 if not stamp:
818 return []
819
820 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
821
822 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
823
824def make_stamp(task, d, file_name = None):
825 """
826 Creates/updates a stamp for a given task
827 (d can be a data dict or dataCache)
828 """
829 cleanmask = stamp_cleanmask_internal(task, d, file_name)
830 for mask in cleanmask:
831 for name in glob.glob(mask):
832 # Preserve sigdata files in the stamps directory
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500833 if "sigdata" in name or "sigbasedata" in name:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834 continue
835 # Preserve taint files in the stamps directory
836 if name.endswith('.taint'):
837 continue
838 os.unlink(name)
839
840 stamp = stamp_internal(task, d, file_name)
841 # Remove the file and recreate to force timestamp
842 # change on broken NFS filesystems
843 if stamp:
844 bb.utils.remove(stamp)
845 open(stamp, "w").close()
846
847 # If we're in task context, write out a signature file for each task
848 # as it completes
849 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
850 stampbase = stamp_internal(task, d, None, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500851 file_name = d.getVar('BB_FILENAME')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500852 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
853
854def del_stamp(task, d, file_name = None):
855 """
856 Removes a stamp for a given task
857 (d can be a data dict or dataCache)
858 """
859 stamp = stamp_internal(task, d, file_name)
860 bb.utils.remove(stamp)
861
862def write_taint(task, d, file_name = None):
863 """
864 Creates a "taint" file which will force the specified task and its
865 dependents to be re-run the next time by influencing the value of its
866 taskhash.
867 (d can be a data dict or dataCache)
868 """
869 import uuid
870 if file_name:
871 taintfn = d.stamp[file_name] + '.' + task + '.taint'
872 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500873 taintfn = d.getVar('STAMP') + '.' + task + '.taint'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500874 bb.utils.mkdirhier(os.path.dirname(taintfn))
875 # The specific content of the taint file is not really important,
876 # we just need it to be random, so a random UUID is used
877 with open(taintfn, 'w') as taintf:
878 taintf.write(str(uuid.uuid4()))
879
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600880def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500881 """
882 Return the stamp for a given task
883 (d can be a data dict or dataCache)
884 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600885 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500886
887def add_tasks(tasklist, d):
888 task_deps = d.getVar('_task_deps', False)
889 if not task_deps:
890 task_deps = {}
891 if not 'tasks' in task_deps:
892 task_deps['tasks'] = []
893 if not 'parents' in task_deps:
894 task_deps['parents'] = {}
895
896 for task in tasklist:
897 task = d.expand(task)
898
899 d.setVarFlag(task, 'task', 1)
900
901 if not task in task_deps['tasks']:
902 task_deps['tasks'].append(task)
903
904 flags = d.getVarFlags(task)
905 def getTask(name):
906 if not name in task_deps:
907 task_deps[name] = {}
908 if name in flags:
909 deptask = d.expand(flags[name])
910 task_deps[name][task] = deptask
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800911 getTask('mcdepends')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500912 getTask('depends')
913 getTask('rdepends')
914 getTask('deptask')
915 getTask('rdeptask')
916 getTask('recrdeptask')
917 getTask('recideptask')
918 getTask('nostamp')
919 getTask('fakeroot')
920 getTask('noexec')
921 getTask('umask')
922 task_deps['parents'][task] = []
923 if 'deps' in flags:
924 for dep in flags['deps']:
Brad Bishopc342db32019-05-15 21:57:59 -0400925 # Check and warn for "addtask task after foo" while foo does not exist
926 #if not dep in tasklist:
927 # bb.warn('%s: dependent task %s for %s does not exist' % (d.getVar('PN'), dep, task))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500928 dep = d.expand(dep)
929 task_deps['parents'][task].append(dep)
930
931 # don't assume holding a reference
932 d.setVar('_task_deps', task_deps)
933
934def addtask(task, before, after, d):
935 if task[:3] != "do_":
936 task = "do_" + task
937
938 d.setVarFlag(task, "task", 1)
939 bbtasks = d.getVar('__BBTASKS', False) or []
940 if task not in bbtasks:
941 bbtasks.append(task)
942 d.setVar('__BBTASKS', bbtasks)
943
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500944 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500945 if after is not None:
946 # set up deps for function
947 for entry in after.split():
948 if entry not in existing:
949 existing.append(entry)
950 d.setVarFlag(task, "deps", existing)
951 if before is not None:
952 # set up things that depend on this func
953 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500954 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500955 if task not in existing:
956 d.setVarFlag(entry, "deps", [task] + existing)
957
958def deltask(task, d):
959 if task[:3] != "do_":
960 task = "do_" + task
961
962 bbtasks = d.getVar('__BBTASKS', False) or []
963 if task in bbtasks:
964 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600965 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500966 d.setVar('__BBTASKS', bbtasks)
967
968 d.delVarFlag(task, 'deps')
969 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500970 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500971 if task in deps:
972 deps.remove(task)
973 d.setVarFlag(bbtask, 'deps', deps)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500974
975def preceedtask(task, with_recrdeptasks, d):
976 """
977 Returns a set of tasks in the current recipe which were specified as
978 precondition by the task itself ("after") or which listed themselves
979 as precondition ("before"). Preceeding tasks specified via the
980 "recrdeptask" are included in the result only if requested. Beware
981 that this may lead to the task itself being listed.
982 """
983 preceed = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400984
985 # Ignore tasks which don't exist
986 tasks = d.getVar('__BBTASKS', False)
987 if task not in tasks:
988 return preceed
989
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500990 preceed.update(d.getVarFlag(task, 'deps') or [])
991 if with_recrdeptasks:
992 recrdeptask = d.getVarFlag(task, 'recrdeptask')
993 if recrdeptask:
994 preceed.update(recrdeptask.split())
995 return preceed
996
997def tasksbetween(task_start, task_end, d):
998 """
999 Return the list of tasks between two tasks in the current recipe,
1000 where task_start is to start at and task_end is the task to end at
1001 (and task_end has a dependency chain back to task_start).
1002 """
1003 outtasks = []
1004 tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
1005 def follow_chain(task, endtask, chain=None):
1006 if not chain:
1007 chain = []
1008 chain.append(task)
1009 for othertask in tasks:
1010 if othertask == task:
1011 continue
1012 if task == endtask:
1013 for ctask in chain:
1014 if ctask not in outtasks:
1015 outtasks.append(ctask)
1016 else:
1017 deps = d.getVarFlag(othertask, 'deps', False)
1018 if task in deps:
1019 follow_chain(othertask, endtask, chain)
1020 chain.pop()
1021 follow_chain(task_start, task_end)
1022 return outtasks