blob: db706d0a3a95b60ba66960340e6ac8e990016dde [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
Patrick Williams92b42cb2022-09-03 06:53:57 -050023import datetime
Patrick Williamsc124f4f2015-09-15 14:41:29 -050024import bb
25import bb.msg
26import bb.process
Patrick Williamsc0f7c042017-02-23 20:41:17 -060027import bb.progress
28from bb import data, event, utils
Patrick Williamsc124f4f2015-09-15 14:41:29 -050029
30bblogger = logging.getLogger('BitBake')
31logger = logging.getLogger('BitBake.Build')
32
Andrew Geisslerc9f78652020-09-18 14:11:35 -050033verboseShellLogging = False
34verboseStdoutLogging = False
35
Patrick Williamsc124f4f2015-09-15 14:41:29 -050036__mtime_cache = {}
37
38def cached_mtime_noerror(f):
39 if f not in __mtime_cache:
40 try:
41 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
42 except OSError:
43 return 0
44 return __mtime_cache[f]
45
46def reset_cache():
47 global __mtime_cache
48 __mtime_cache = {}
49
50# When we execute a Python function, we'd like certain things
51# in all namespaces, hence we add them to __builtins__.
52# If we do not do this and use the exec globals, they will
53# not be available to subfunctions.
Patrick Williamsc0f7c042017-02-23 20:41:17 -060054if hasattr(__builtins__, '__setitem__'):
55 builtins = __builtins__
56else:
57 builtins = __builtins__.__dict__
58
59builtins['bb'] = bb
60builtins['os'] = os
Patrick Williamsc124f4f2015-09-15 14:41:29 -050061
Patrick Williamsc124f4f2015-09-15 14:41:29 -050062class TaskBase(event.Event):
63 """Base class for task events"""
64
Andrew Geissler82c905d2020-04-13 13:39:40 -050065 def __init__(self, t, fn, logfile, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050066 self._task = t
Andrew Geissler82c905d2020-04-13 13:39:40 -050067 self._fn = fn
Brad Bishop6e60e8b2018-02-01 10:27:11 -050068 self._package = d.getVar("PF")
69 self._mc = d.getVar("BB_CURRENT_MC")
70 self.taskfile = d.getVar("FILE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050071 self.taskname = self._task
72 self.logfile = logfile
73 self.time = time.time()
Andrew Geissler82c905d2020-04-13 13:39:40 -050074 self.pn = d.getVar("PN")
75 self.pv = d.getVar("PV")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050076 event.Event.__init__(self)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050077 self._message = "recipe %s: task %s: %s" % (d.getVar("PF"), t, self.getDisplayName())
Patrick Williamsc124f4f2015-09-15 14:41:29 -050078
79 def getTask(self):
80 return self._task
81
82 def setTask(self, task):
83 self._task = task
84
85 def getDisplayName(self):
86 return bb.event.getName(self)[4:]
87
88 task = property(getTask, setTask, None, "task property")
89
90class TaskStarted(TaskBase):
91 """Task execution started"""
Andrew Geissler82c905d2020-04-13 13:39:40 -050092 def __init__(self, t, fn, logfile, taskflags, d):
93 super(TaskStarted, self).__init__(t, fn, logfile, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050094 self.taskflags = taskflags
95
96class TaskSucceeded(TaskBase):
97 """Task execution completed"""
98
99class TaskFailed(TaskBase):
100 """Task execution failed"""
101
Andrew Geissler82c905d2020-04-13 13:39:40 -0500102 def __init__(self, task, fn, logfile, metadata, errprinted = False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500103 self.errprinted = errprinted
Andrew Geissler82c905d2020-04-13 13:39:40 -0500104 super(TaskFailed, self).__init__(task, fn, logfile, metadata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500105
106class TaskFailedSilent(TaskBase):
107 """Task execution failed (silently)"""
108 def getDisplayName(self):
109 # Don't need to tell the user it was silent
110 return "Failed"
111
112class TaskInvalid(TaskBase):
113
Andrew Geissler82c905d2020-04-13 13:39:40 -0500114 def __init__(self, task, fn, metadata):
115 super(TaskInvalid, self).__init__(task, fn, None, metadata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500116 self._message = "No such task '%s'" % task
117
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600118class TaskProgress(event.Event):
119 """
120 Task made some progress that could be reported to the user, usually in
121 the form of a progress bar or similar.
122 NOTE: this class does not inherit from TaskBase since it doesn't need
123 to - it's fired within the task context itself, so we don't have any of
124 the context information that you do in the case of the other events.
125 The event PID can be used to determine which task it came from.
126 The progress value is normally 0-100, but can also be negative
127 indicating that progress has been made but we aren't able to determine
128 how much.
129 The rate is optional, this is simply an extra string to display to the
130 user if specified.
131 """
132 def __init__(self, progress, rate=None):
133 self.progress = progress
134 self.rate = rate
135 event.Event.__init__(self)
136
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500137
138class LogTee(object):
139 def __init__(self, logger, outfile):
140 self.outfile = outfile
141 self.logger = logger
142 self.name = self.outfile.name
143
144 def write(self, string):
145 self.logger.plain(string)
146 self.outfile.write(string)
147
148 def __enter__(self):
149 self.outfile.__enter__()
150 return self
151
152 def __exit__(self, *excinfo):
153 self.outfile.__exit__(*excinfo)
154
155 def __repr__(self):
156 return '<LogTee {0}>'.format(self.name)
Brad Bishop15ae2502019-06-18 21:44:24 -0400157
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500158 def flush(self):
159 self.outfile.flush()
160
Brad Bishop15ae2502019-06-18 21:44:24 -0400161
162class StdoutNoopContextManager:
163 """
164 This class acts like sys.stdout, but adds noop __enter__ and __exit__ methods.
165 """
166 def __enter__(self):
167 return sys.stdout
168
169 def __exit__(self, *exc_info):
170 pass
171
172 def write(self, string):
173 return sys.stdout.write(string)
174
175 def flush(self):
176 sys.stdout.flush()
177
178 @property
179 def name(self):
180 return sys.stdout.name
181
182
Brad Bishop08902b02019-08-20 09:16:51 -0400183def exec_func(func, d, dirs = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500184 """Execute a BB 'function'"""
185
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600186 try:
187 oldcwd = os.getcwd()
188 except:
189 oldcwd = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500190
191 flags = d.getVarFlags(func)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500192 cleandirs = flags.get('cleandirs') if flags else None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500193 if cleandirs:
194 for cdir in d.expand(cleandirs).split():
195 bb.utils.remove(cdir, True)
196 bb.utils.mkdirhier(cdir)
197
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500198 if flags and dirs is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500199 dirs = flags.get('dirs')
200 if dirs:
201 dirs = d.expand(dirs).split()
202
203 if dirs:
204 for adir in dirs:
205 bb.utils.mkdirhier(adir)
206 adir = dirs[-1]
207 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600208 adir = None
209
210 body = d.getVar(func, False)
211 if not body:
212 if body is None:
213 logger.warning("Function %s doesn't exist", func)
214 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500215
216 ispython = flags.get('python')
217
218 lockflag = flags.get('lockfiles')
219 if lockflag:
220 lockfiles = [f for f in d.expand(lockflag).split()]
221 else:
222 lockfiles = None
223
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500224 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500225
226 # or func allows items to be executed outside of the normal
227 # task set, such as buildhistory
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500228 task = d.getVar('BB_RUNTASK') or func
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500229 if task == func:
230 taskfunc = task
231 else:
232 taskfunc = "%s.%s" % (task, func)
233
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500234 runfmt = d.getVar('BB_RUNFMT') or "run.{func}.{pid}"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500235 runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
236 runfile = os.path.join(tempdir, runfn)
237 bb.utils.mkdirhier(os.path.dirname(runfile))
238
239 # Setup the courtesy link to the runfn, only for tasks
240 # we create the link 'just' before the run script is created
241 # if we create it after, and if the run script fails, then the
242 # link won't be created as an exception would be fired.
243 if task == func:
244 runlink = os.path.join(tempdir, 'run.{0}'.format(task))
245 if runlink:
246 bb.utils.remove(runlink)
247
248 try:
249 os.symlink(runfn, runlink)
250 except OSError:
251 pass
252
253 with bb.utils.fileslocked(lockfiles):
254 if ispython:
Brad Bishop08902b02019-08-20 09:16:51 -0400255 exec_func_python(func, d, runfile, cwd=adir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500256 else:
257 exec_func_shell(func, d, runfile, cwd=adir)
258
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600259 try:
260 curcwd = os.getcwd()
261 except:
262 curcwd = None
263
264 if oldcwd and curcwd != oldcwd:
265 try:
266 bb.warn("Task %s changed cwd to %s" % (func, curcwd))
267 os.chdir(oldcwd)
268 except:
269 pass
270
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271_functionfmt = """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500272{function}(d)
273"""
274logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
Brad Bishop08902b02019-08-20 09:16:51 -0400275def exec_func_python(func, d, runfile, cwd=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500276 """Execute a python BB 'function'"""
277
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500278 code = _functionfmt.format(function=func)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500279 bb.utils.mkdirhier(os.path.dirname(runfile))
280 with open(runfile, 'w') as script:
281 bb.data.emit_func_python(func, script, d)
282
283 if cwd:
284 try:
285 olddir = os.getcwd()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600286 except OSError as e:
287 bb.warn("%s: Cannot get cwd: %s" % (func, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500288 olddir = None
289 os.chdir(cwd)
290
291 bb.debug(2, "Executing python function %s" % func)
292
293 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500294 text = "def %s(d):\n%s" % (func, d.getVar(func, False))
295 fn = d.getVarFlag(func, "filename", False)
296 lineno = int(d.getVarFlag(func, "lineno", False))
297 bb.methodpool.insert_method(func, text, fn, lineno - 1)
298
Andrew Geissler5199d832021-09-24 16:47:35 -0500299 comp = utils.better_compile(code, func, "exec_func_python() autogenerated")
300 utils.better_exec(comp, {"d": d}, code, "exec_func_python() autogenerated")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500301 finally:
Andrew Geissler5199d832021-09-24 16:47:35 -0500302 # We want any stdout/stderr to be printed before any other log messages to make debugging
303 # more accurate. In some cases we seem to lose stdout/stderr entirely in logging tests without this.
304 sys.stdout.flush()
305 sys.stderr.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500306 bb.debug(2, "Python function %s finished" % func)
307
308 if cwd and olddir:
309 try:
310 os.chdir(olddir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600311 except OSError as e:
312 bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500313
314def shell_trap_code():
315 return '''#!/bin/sh\n
Andrew Geissler635e0e42020-08-21 15:58:33 -0500316__BITBAKE_LAST_LINE=0
317
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500318# Emit a useful diagnostic if something fails:
Andrew Geissler635e0e42020-08-21 15:58:33 -0500319bb_sh_exit_handler() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500320 ret=$?
Andrew Geissler635e0e42020-08-21 15:58:33 -0500321 if [ "$ret" != 0 ]; then
322 echo "WARNING: exit code $ret from a shell command."
323 fi
324 exit $ret
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500325}
Andrew Geissler635e0e42020-08-21 15:58:33 -0500326
327bb_bash_exit_handler() {
328 ret=$?
329 { set +x; } > /dev/null
330 trap "" DEBUG
331 if [ "$ret" != 0 ]; then
332 echo "WARNING: ${BASH_SOURCE[0]}:${__BITBAKE_LAST_LINE} exit $ret from '$1'"
333
334 echo "WARNING: Backtrace (BB generated script): "
335 for i in $(seq 1 $((${#FUNCNAME[@]} - 1))); do
336 if [ "$i" -eq 1 ]; then
337 echo -e "\t#$((i)): ${FUNCNAME[$i]}, ${BASH_SOURCE[$((i-1))]}, line ${__BITBAKE_LAST_LINE}"
338 else
339 echo -e "\t#$((i)): ${FUNCNAME[$i]}, ${BASH_SOURCE[$((i-1))]}, line ${BASH_LINENO[$((i-1))]}"
340 fi
341 done
342 fi
343 exit $ret
344}
345
346bb_bash_debug_handler() {
347 local line=${BASH_LINENO[0]}
348 # For some reason the DEBUG trap trips with lineno=1 when scripts exit; ignore it
349 if [ "$line" -eq 1 ]; then
350 return
351 fi
352
353 # Track the line number of commands as they execute. This is so we can have access to the failing line number
354 # 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
355 if [ "${FUNCNAME[1]}" != "bb_bash_exit_handler" ]; then
356 __BITBAKE_LAST_LINE=$line
357 fi
358}
359
360case $BASH_VERSION in
361"") trap 'bb_sh_exit_handler' 0
362 set -e
363 ;;
364*) trap 'bb_bash_exit_handler "$BASH_COMMAND"' 0
365 trap '{ bb_bash_debug_handler; } 2>/dev/null' DEBUG
366 set -e
367 shopt -s extdebug
368 ;;
369esac
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500370'''
371
Brad Bishop15ae2502019-06-18 21:44:24 -0400372def create_progress_handler(func, progress, logfile, d):
373 if progress == 'percent':
374 # Use default regex
375 return bb.progress.BasicProgressHandler(d, outfile=logfile)
376 elif progress.startswith('percent:'):
377 # Use specified regex
378 return bb.progress.BasicProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
379 elif progress.startswith('outof:'):
380 # Use specified regex
381 return bb.progress.OutOfProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
382 elif progress.startswith("custom:"):
383 # Use a custom progress handler that was injected via OE_EXTRA_IMPORTS or __builtins__
384 import functools
385 from types import ModuleType
386
387 parts = progress.split(":", 2)
388 _, cls, otherargs = parts[0], parts[1], (parts[2] or None) if parts[2:] else None
389 if cls:
390 def resolve(x, y):
391 if not x:
392 return None
393 if isinstance(x, ModuleType):
394 return getattr(x, y, None)
395 return x.get(y)
396 cls_obj = functools.reduce(resolve, cls.split("."), bb.utils._context)
397 if not cls_obj:
398 # Fall-back on __builtins__
Andrew Geissler635e0e42020-08-21 15:58:33 -0500399 cls_obj = functools.reduce(resolve, cls.split("."), __builtins__)
Brad Bishop15ae2502019-06-18 21:44:24 -0400400 if cls_obj:
401 return cls_obj(d, outfile=logfile, otherargs=otherargs)
402 bb.warn('%s: unknown custom progress handler in task progress varflag value "%s", ignoring' % (func, cls))
403 else:
404 bb.warn('%s: invalid task progress varflag value "%s", ignoring' % (func, progress))
405
406 return logfile
407
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500408def exec_func_shell(func, d, runfile, cwd=None):
409 """Execute a shell function from the metadata
410
411 Note on directory behavior. The 'dirs' varflag should contain a list
412 of the directories you need created prior to execution. The last
413 item in the list is where we will chdir/cd to.
414 """
415
416 # Don't let the emitted shell script override PWD
417 d.delVarFlag('PWD', 'export')
418
419 with open(runfile, 'w') as script:
420 script.write(shell_trap_code())
421
422 bb.data.emit_func(func, script, d)
423
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500424 if verboseShellLogging or bb.utils.to_boolean(d.getVar("BB_VERBOSE_LOGS", False)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500425 script.write("set -x\n")
426 if cwd:
427 script.write("cd '%s'\n" % cwd)
428 script.write("%s\n" % func)
429 script.write('''
430# cleanup
431ret=$?
432trap '' 0
433exit $ret
434''')
435
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600436 os.chmod(runfile, 0o775)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500437
438 cmd = runfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500439 if d.getVarFlag(func, 'fakeroot', False):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500440 fakerootcmd = d.getVar('FAKEROOT')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500441 if fakerootcmd:
442 cmd = [fakerootcmd, runfile]
443
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500444 if verboseStdoutLogging:
Brad Bishop15ae2502019-06-18 21:44:24 -0400445 logfile = LogTee(logger, StdoutNoopContextManager())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400447 logfile = StdoutNoopContextManager()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500448
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500449 progress = d.getVarFlag(func, 'progress')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600450 if progress:
Andrew Geissler635e0e42020-08-21 15:58:33 -0500451 try:
452 logfile = create_progress_handler(func, progress, logfile, d)
453 except:
454 from traceback import format_exc
455 logger.error("Failed to create progress handler")
456 logger.error(format_exc())
457 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600458
459 fifobuffer = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500460 def readfifo(data):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600461 nonlocal fifobuffer
462 fifobuffer.extend(data)
463 while fifobuffer:
464 message, token, nextmsg = fifobuffer.partition(b"\00")
465 if token:
466 splitval = message.split(b' ', 1)
467 cmd = splitval[0].decode("utf-8")
468 if len(splitval) > 1:
469 value = splitval[1].decode("utf-8")
470 else:
471 value = ''
472 if cmd == 'bbplain':
473 bb.plain(value)
474 elif cmd == 'bbnote':
475 bb.note(value)
Brad Bishopc342db32019-05-15 21:57:59 -0400476 elif cmd == 'bbverbnote':
477 bb.verbnote(value)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600478 elif cmd == 'bbwarn':
479 bb.warn(value)
480 elif cmd == 'bberror':
481 bb.error(value)
482 elif cmd == 'bbfatal':
483 # The caller will call exit themselves, so bb.error() is
484 # what we want here rather than bb.fatal()
485 bb.error(value)
486 elif cmd == 'bbfatal_log':
487 bb.error(value, forcelog=True)
488 elif cmd == 'bbdebug':
489 splitval = value.split(' ', 1)
490 level = int(splitval[0])
491 value = splitval[1]
492 bb.debug(level, value)
493 else:
494 bb.warn("Unrecognised command '%s' on FIFO" % cmd)
495 fifobuffer = nextmsg
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500496 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600497 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500498
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500499 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500500 fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
501 if os.path.exists(fifopath):
502 os.unlink(fifopath)
503 os.mkfifo(fifopath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600504 with open(fifopath, 'r+b', buffering=0) as fifo:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500505 try:
506 bb.debug(2, "Executing shell function %s" % func)
Brad Bishop08902b02019-08-20 09:16:51 -0400507 with open(os.devnull, 'r+') as stdin, logfile:
508 bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
Andrew Geissler635e0e42020-08-21 15:58:33 -0500509 except bb.process.ExecutionError as exe:
510 # Find the backtrace that the shell trap generated
511 backtrace_marker_regex = re.compile(r"WARNING: Backtrace \(BB generated script\)")
512 stdout_lines = (exe.stdout or "").split("\n")
513 backtrace_start_line = None
514 for i, line in enumerate(reversed(stdout_lines)):
515 if backtrace_marker_regex.search(line):
516 backtrace_start_line = len(stdout_lines) - i
517 break
518
519 # Read the backtrace frames, starting at the location we just found
520 backtrace_entry_regex = re.compile(r"#(?P<frameno>\d+): (?P<funcname>[^\s]+), (?P<file>.+?), line ("
521 r"?P<lineno>\d+)")
522 backtrace_frames = []
523 if backtrace_start_line:
524 for line in itertools.islice(stdout_lines, backtrace_start_line, None):
525 match = backtrace_entry_regex.search(line)
526 if match:
527 backtrace_frames.append(match.groupdict())
528
529 with open(runfile, "r") as script:
530 script_lines = [line.rstrip() for line in script.readlines()]
531
532 # For each backtrace frame, search backwards in the script (from the line number called out by the frame),
533 # to find the comment that emit_vars injected when it wrote the script. This will give us the metadata
534 # filename (e.g. .bb or .bbclass) and line number where the shell function was originally defined.
535 script_metadata_comment_regex = re.compile(r"# line: (?P<lineno>\d+), file: (?P<file>.+)")
536 better_frames = []
537 # Skip the very last frame since it's just the call to the shell task in the body of the script
538 for frame in backtrace_frames[:-1]:
539 # Check whether the frame corresponds to a function defined in the script vs external script.
540 if os.path.samefile(frame["file"], runfile):
541 # Search backwards from the frame lineno to locate the comment that BB injected
542 i = int(frame["lineno"]) - 1
543 while i >= 0:
544 match = script_metadata_comment_regex.match(script_lines[i])
545 if match:
546 # Calculate the relative line in the function itself
547 relative_line_in_function = int(frame["lineno"]) - i - 2
548 # Calculate line in the function as declared in the metadata
549 metadata_function_line = relative_line_in_function + int(match["lineno"])
550 better_frames.append("#{frameno}: {funcname}, {file}, line {lineno}".format(
551 frameno=frame["frameno"],
552 funcname=frame["funcname"],
553 file=match["file"],
554 lineno=metadata_function_line
555 ))
556 break
557 i -= 1
558 else:
559 better_frames.append("#{frameno}: {funcname}, {file}, line {lineno}".format(**frame))
560
561 if better_frames:
562 better_frames = ("\t{0}".format(frame) for frame in better_frames)
563 exe.extra_message = "\nBacktrace (metadata-relative locations):\n{0}".format("\n".join(better_frames))
564 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500565 finally:
566 os.unlink(fifopath)
567
568 bb.debug(2, "Shell function %s finished" % func)
569
570def _task_data(fn, task, d):
571 localdata = bb.data.createCopy(d)
572 localdata.setVar('BB_FILENAME', fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573 localdata.setVar('OVERRIDES', 'task-%s:%s' %
574 (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500575 bb.data.expandKeys(localdata)
576 return localdata
577
578def _exec_task(fn, task, d, quieterr):
579 """Execute a BB 'task'
580
581 Execution of a task involves a bit more setup than executing a function,
582 running it with its own local metadata, and with some useful variables set.
583 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500584 if not d.getVarFlag(task, 'task', False):
Andrew Geissler9aee5002022-03-30 16:27:02 +0000585 event.fire(TaskInvalid(task, fn, d), d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500586 logger.error("No such task: %s" % task)
587 return 1
588
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600589 logger.debug("Executing task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500590
591 localdata = _task_data(fn, task, d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500592 tempdir = localdata.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500593 if not tempdir:
594 bb.fatal("T variable not set, unable to build")
595
596 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500597 nice = localdata.getVar("BB_TASK_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500598 if nice:
599 curnice = os.nice(0)
600 nice = int(nice) - curnice
601 newnice = os.nice(nice)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600602 logger.debug("Renice to %s " % newnice)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500603 ionice = localdata.getVar("BB_TASK_IONICE_LEVEL")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500604 if ionice:
605 try:
606 cls, prio = ionice.split(".", 1)
607 bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
608 except:
609 bb.warn("Invalid ionice level %s" % ionice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500610
611 bb.utils.mkdirhier(tempdir)
612
613 # Determine the logfile to generate
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500614 logfmt = localdata.getVar('BB_LOGFMT') or 'log.{task}.{pid}'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500615 logbase = logfmt.format(task=task, pid=os.getpid())
616
617 # Document the order of the tasks...
618 logorder = os.path.join(tempdir, 'log.task_order')
619 try:
620 with open(logorder, 'a') as logorderfile:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500621 timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S.%f")
622 logorderfile.write('{0} {1} ({2}): {3}\n'.format(timestamp, task, os.getpid(), logbase))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500623 except OSError:
624 logger.exception("Opening log file '%s'", logorder)
625 pass
626
627 # Setup the courtesy link to the logfn
628 loglink = os.path.join(tempdir, 'log.{0}'.format(task))
629 logfn = os.path.join(tempdir, logbase)
630 if loglink:
631 bb.utils.remove(loglink)
632
633 try:
634 os.symlink(logbase, loglink)
635 except OSError:
636 pass
637
638 prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
639 postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
640
641 class ErrorCheckHandler(logging.Handler):
642 def __init__(self):
643 self.triggered = False
644 logging.Handler.__init__(self, logging.ERROR)
645 def emit(self, record):
646 if getattr(record, 'forcelog', False):
647 self.triggered = False
648 else:
649 self.triggered = True
650
651 # Handle logfiles
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500652 try:
653 bb.utils.mkdirhier(os.path.dirname(logfn))
654 logfile = open(logfn, 'w')
655 except OSError:
656 logger.exception("Opening log file '%s'", logfn)
657 pass
658
659 # Dup the existing fds so we dont lose them
660 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
661 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
662 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
663
664 # Replace those fds with our own
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800665 with open('/dev/null', 'r') as si:
666 os.dup2(si.fileno(), osi[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500667 os.dup2(logfile.fileno(), oso[1])
668 os.dup2(logfile.fileno(), ose[1])
669
670 # Ensure Python logging goes to the logfile
671 handler = logging.StreamHandler(logfile)
672 handler.setFormatter(logformatter)
673 # Always enable full debug output into task logfiles
674 handler.setLevel(logging.DEBUG - 2)
675 bblogger.addHandler(handler)
676
677 errchk = ErrorCheckHandler()
678 bblogger.addHandler(errchk)
679
680 localdata.setVar('BB_LOGFILE', logfn)
681 localdata.setVar('BB_RUNTASK', task)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500682 localdata.setVar('BB_TASK_LOGGER', bblogger)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500683
684 flags = localdata.getVarFlags(task)
685
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500686 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600687 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500688 event.fire(TaskStarted(task, fn, logfn, flags, localdata), localdata)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600689
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600690 for func in (prefuncs or '').split():
691 exec_func(func, localdata)
692 exec_func(task, localdata)
693 for func in (postfuncs or '').split():
694 exec_func(func, localdata)
Andrew Geissler5199d832021-09-24 16:47:35 -0500695 finally:
696 # Need to flush and close the logs before sending events where the
697 # UI may try to look at the logs.
698 sys.stdout.flush()
699 sys.stderr.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500700
Andrew Geissler5199d832021-09-24 16:47:35 -0500701 bblogger.removeHandler(handler)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500702
Andrew Geissler5199d832021-09-24 16:47:35 -0500703 # Restore the backup fds
704 os.dup2(osi[0], osi[1])
705 os.dup2(oso[0], oso[1])
706 os.dup2(ose[0], ose[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707
Andrew Geissler5199d832021-09-24 16:47:35 -0500708 # Close the backup fds
709 os.close(osi[0])
710 os.close(oso[0])
711 os.close(ose[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500712
Andrew Geissler5199d832021-09-24 16:47:35 -0500713 logfile.close()
714 if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
715 logger.debug2("Zero size logfn %s, removing", logfn)
716 bb.utils.remove(logfn)
717 bb.utils.remove(loglink)
Andrew Geissler5199d832021-09-24 16:47:35 -0500718 except (Exception, SystemExit) as exc:
Andrew Geissler595f6302022-01-24 19:11:47 +0000719 handled = False
720 if isinstance(exc, bb.BBHandledException):
721 handled = True
722
Andrew Geissler5199d832021-09-24 16:47:35 -0500723 if quieterr:
Andrew Geissler595f6302022-01-24 19:11:47 +0000724 if not handled:
725 logger.warning(repr(exc))
Andrew Geissler5199d832021-09-24 16:47:35 -0500726 event.fire(TaskFailedSilent(task, fn, logfn, localdata), localdata)
727 else:
728 errprinted = errchk.triggered
729 # If the output is already on stdout, we've printed the information in the
730 # logs once already so don't duplicate
Andrew Geissler595f6302022-01-24 19:11:47 +0000731 if verboseStdoutLogging or handled:
Andrew Geissler5199d832021-09-24 16:47:35 -0500732 errprinted = True
Andrew Geissler595f6302022-01-24 19:11:47 +0000733 if not handled:
734 logger.error(repr(exc))
Andrew Geissler5199d832021-09-24 16:47:35 -0500735 event.fire(TaskFailed(task, fn, logfn, localdata, errprinted), localdata)
736 return 1
737
Andrew Geissler82c905d2020-04-13 13:39:40 -0500738 event.fire(TaskSucceeded(task, fn, logfn, localdata), localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500739
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500740 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500741 make_stamp(task, localdata)
742
743 return 0
744
745def exec_task(fn, task, d, profile = False):
746 try:
747 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500748 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749 quieterr = True
750
751 if profile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500752 profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500753 try:
754 import cProfile as profile
755 except:
756 import profile
757 prof = profile.Profile()
758 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
759 prof.dump_stats(profname)
760 bb.utils.process_profilelog(profname)
761
762 return ret
763 else:
764 return _exec_task(fn, task, d, quieterr)
765
766 except Exception:
767 from traceback import format_exc
768 if not quieterr:
769 logger.error("Build of %s failed" % (task))
770 logger.error(format_exc())
771 failedevent = TaskFailed(task, None, d, True)
772 event.fire(failedevent, d)
773 return 1
774
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600775def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500776 """
777 Internal stamp helper function
778 Makes sure the stamp directory exists
779 Returns the stamp path+filename
780
781 In the bitbake core, d can be a CacheData and file_name will be set.
782 When called in task context, d will be a data store, file_name will not be set
783 """
784 taskflagname = taskname
785 if taskname.endswith("_setscene") and taskname != "do_setscene":
786 taskflagname = taskname.replace("_setscene", "")
787
788 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500789 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500790 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
791 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500792 stamp = d.getVar('STAMP')
793 file_name = d.getVar('BB_FILENAME')
794 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500795
796 if baseonly:
797 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 if noextra:
799 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500800
801 if not stamp:
802 return
803
804 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
805
806 stampdir = os.path.dirname(stamp)
807 if cached_mtime_noerror(stampdir) == 0:
808 bb.utils.mkdirhier(stampdir)
809
810 return stamp
811
812def stamp_cleanmask_internal(taskname, d, file_name):
813 """
814 Internal stamp helper function to generate stamp cleaning mask
815 Returns the stamp path+filename
816
817 In the bitbake core, d can be a CacheData and file_name will be set.
818 When called in task context, d will be a data store, file_name will not be set
819 """
820 taskflagname = taskname
821 if taskname.endswith("_setscene") and taskname != "do_setscene":
822 taskflagname = taskname.replace("_setscene", "")
823
824 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500825 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500826 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
827 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500828 stamp = d.getVar('STAMPCLEAN')
829 file_name = d.getVar('BB_FILENAME')
830 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500831
832 if not stamp:
833 return []
834
835 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
836
837 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
838
Andrew Geisslerd5838332022-05-27 11:33:10 -0500839def clean_stamp(task, d, file_name = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500840 cleanmask = stamp_cleanmask_internal(task, d, file_name)
841 for mask in cleanmask:
842 for name in glob.glob(mask):
843 # Preserve sigdata files in the stamps directory
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500844 if "sigdata" in name or "sigbasedata" in name:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500845 continue
846 # Preserve taint files in the stamps directory
847 if name.endswith('.taint'):
848 continue
849 os.unlink(name)
Andrew Geisslerd5838332022-05-27 11:33:10 -0500850 return
851
852def make_stamp(task, d, file_name = None):
853 """
854 Creates/updates a stamp for a given task
855 (d can be a data dict or dataCache)
856 """
857 clean_stamp(task, d, file_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500858
859 stamp = stamp_internal(task, d, file_name)
860 # Remove the file and recreate to force timestamp
861 # change on broken NFS filesystems
862 if stamp:
863 bb.utils.remove(stamp)
864 open(stamp, "w").close()
865
866 # If we're in task context, write out a signature file for each task
867 # as it completes
868 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
869 stampbase = stamp_internal(task, d, None, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500870 file_name = d.getVar('BB_FILENAME')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500871 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
872
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500873def find_stale_stamps(task, d, file_name=None):
874 current = stamp_internal(task, d, file_name)
875 current2 = stamp_internal(task + "_setscene", d, file_name)
876 cleanmask = stamp_cleanmask_internal(task, d, file_name)
877 found = []
878 for mask in cleanmask:
879 for name in glob.glob(mask):
880 if "sigdata" in name or "sigbasedata" in name:
881 continue
882 if name.endswith('.taint'):
883 continue
884 if name == current or name == current2:
885 continue
886 logger.debug2("Stampfile %s does not match %s or %s" % (name, current, current2))
887 found.append(name)
888 return found
889
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500890def del_stamp(task, d, file_name = None):
891 """
892 Removes a stamp for a given task
893 (d can be a data dict or dataCache)
894 """
895 stamp = stamp_internal(task, d, file_name)
896 bb.utils.remove(stamp)
897
898def write_taint(task, d, file_name = None):
899 """
900 Creates a "taint" file which will force the specified task and its
901 dependents to be re-run the next time by influencing the value of its
902 taskhash.
903 (d can be a data dict or dataCache)
904 """
905 import uuid
906 if file_name:
907 taintfn = d.stamp[file_name] + '.' + task + '.taint'
908 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500909 taintfn = d.getVar('STAMP') + '.' + task + '.taint'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500910 bb.utils.mkdirhier(os.path.dirname(taintfn))
911 # The specific content of the taint file is not really important,
912 # we just need it to be random, so a random UUID is used
913 with open(taintfn, 'w') as taintf:
914 taintf.write(str(uuid.uuid4()))
915
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600916def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500917 """
918 Return the stamp for a given task
919 (d can be a data dict or dataCache)
920 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600921 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500922
923def add_tasks(tasklist, d):
924 task_deps = d.getVar('_task_deps', False)
925 if not task_deps:
926 task_deps = {}
927 if not 'tasks' in task_deps:
928 task_deps['tasks'] = []
929 if not 'parents' in task_deps:
930 task_deps['parents'] = {}
931
932 for task in tasklist:
933 task = d.expand(task)
934
935 d.setVarFlag(task, 'task', 1)
936
937 if not task in task_deps['tasks']:
938 task_deps['tasks'].append(task)
939
940 flags = d.getVarFlags(task)
941 def getTask(name):
942 if not name in task_deps:
943 task_deps[name] = {}
944 if name in flags:
945 deptask = d.expand(flags[name])
Andrew Geissler09036742021-06-25 14:25:14 -0500946 if name in ['noexec', 'fakeroot', 'nostamp']:
947 if deptask != '1':
948 bb.warn("In a future version of BitBake, setting the '{}' flag to something other than '1' "
949 "will result in the flag not being set. See YP bug #13808.".format(name))
950
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500951 task_deps[name][task] = deptask
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800952 getTask('mcdepends')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500953 getTask('depends')
954 getTask('rdepends')
955 getTask('deptask')
956 getTask('rdeptask')
957 getTask('recrdeptask')
958 getTask('recideptask')
959 getTask('nostamp')
960 getTask('fakeroot')
961 getTask('noexec')
962 getTask('umask')
963 task_deps['parents'][task] = []
964 if 'deps' in flags:
965 for dep in flags['deps']:
Brad Bishopc342db32019-05-15 21:57:59 -0400966 # Check and warn for "addtask task after foo" while foo does not exist
967 #if not dep in tasklist:
968 # bb.warn('%s: dependent task %s for %s does not exist' % (d.getVar('PN'), dep, task))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500969 dep = d.expand(dep)
970 task_deps['parents'][task].append(dep)
971
972 # don't assume holding a reference
973 d.setVar('_task_deps', task_deps)
974
975def addtask(task, before, after, d):
976 if task[:3] != "do_":
977 task = "do_" + task
978
979 d.setVarFlag(task, "task", 1)
980 bbtasks = d.getVar('__BBTASKS', False) or []
981 if task not in bbtasks:
982 bbtasks.append(task)
983 d.setVar('__BBTASKS', bbtasks)
984
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500985 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500986 if after is not None:
987 # set up deps for function
988 for entry in after.split():
989 if entry not in existing:
990 existing.append(entry)
991 d.setVarFlag(task, "deps", existing)
992 if before is not None:
993 # set up things that depend on this func
994 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500995 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500996 if task not in existing:
997 d.setVarFlag(entry, "deps", [task] + existing)
998
999def deltask(task, d):
1000 if task[:3] != "do_":
1001 task = "do_" + task
1002
1003 bbtasks = d.getVar('__BBTASKS', False) or []
1004 if task in bbtasks:
1005 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001006 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001007 d.setVar('__BBTASKS', bbtasks)
1008
1009 d.delVarFlag(task, 'deps')
1010 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001011 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001012 if task in deps:
1013 deps.remove(task)
1014 d.setVarFlag(bbtask, 'deps', deps)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001015
1016def preceedtask(task, with_recrdeptasks, d):
1017 """
1018 Returns a set of tasks in the current recipe which were specified as
1019 precondition by the task itself ("after") or which listed themselves
1020 as precondition ("before"). Preceeding tasks specified via the
1021 "recrdeptask" are included in the result only if requested. Beware
1022 that this may lead to the task itself being listed.
1023 """
1024 preceed = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -04001025
1026 # Ignore tasks which don't exist
1027 tasks = d.getVar('__BBTASKS', False)
1028 if task not in tasks:
1029 return preceed
1030
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001031 preceed.update(d.getVarFlag(task, 'deps') or [])
1032 if with_recrdeptasks:
1033 recrdeptask = d.getVarFlag(task, 'recrdeptask')
1034 if recrdeptask:
1035 preceed.update(recrdeptask.split())
1036 return preceed
1037
1038def tasksbetween(task_start, task_end, d):
1039 """
1040 Return the list of tasks between two tasks in the current recipe,
1041 where task_start is to start at and task_end is the task to end at
1042 (and task_end has a dependency chain back to task_start).
1043 """
1044 outtasks = []
1045 tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
1046 def follow_chain(task, endtask, chain=None):
1047 if not chain:
1048 chain = []
Andrew Geissler5199d832021-09-24 16:47:35 -05001049 if task in chain:
1050 bb.fatal("Circular task dependencies as %s depends on itself via the chain %s" % (task, " -> ".join(chain)))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001051 chain.append(task)
1052 for othertask in tasks:
1053 if othertask == task:
1054 continue
1055 if task == endtask:
1056 for ctask in chain:
1057 if ctask not in outtasks:
1058 outtasks.append(ctask)
1059 else:
1060 deps = d.getVarFlag(othertask, 'deps', False)
1061 if task in deps:
1062 follow_chain(othertask, endtask, chain)
1063 chain.pop()
1064 follow_chain(task_start, task_end)
1065 return outtasks