blob: b8c1099ef5731c43b1e0b451dc6b4b4abde32d6b [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)))
575 localdata.finalize()
576 bb.data.expandKeys(localdata)
577 return localdata
578
579def _exec_task(fn, task, d, quieterr):
580 """Execute a BB 'task'
581
582 Execution of a task involves a bit more setup than executing a function,
583 running it with its own local metadata, and with some useful variables set.
584 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500585 if not d.getVarFlag(task, 'task', False):
Andrew Geissler9aee5002022-03-30 16:27:02 +0000586 event.fire(TaskInvalid(task, fn, d), d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500587 logger.error("No such task: %s" % task)
588 return 1
589
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600590 logger.debug("Executing task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500591
592 localdata = _task_data(fn, task, d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500593 tempdir = localdata.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500594 if not tempdir:
595 bb.fatal("T variable not set, unable to build")
596
597 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500598 nice = localdata.getVar("BB_TASK_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500599 if nice:
600 curnice = os.nice(0)
601 nice = int(nice) - curnice
602 newnice = os.nice(nice)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600603 logger.debug("Renice to %s " % newnice)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500604 ionice = localdata.getVar("BB_TASK_IONICE_LEVEL")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500605 if ionice:
606 try:
607 cls, prio = ionice.split(".", 1)
608 bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
609 except:
610 bb.warn("Invalid ionice level %s" % ionice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500611
612 bb.utils.mkdirhier(tempdir)
613
614 # Determine the logfile to generate
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500615 logfmt = localdata.getVar('BB_LOGFMT') or 'log.{task}.{pid}'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616 logbase = logfmt.format(task=task, pid=os.getpid())
617
618 # Document the order of the tasks...
619 logorder = os.path.join(tempdir, 'log.task_order')
620 try:
621 with open(logorder, 'a') as logorderfile:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500622 timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S.%f")
623 logorderfile.write('{0} {1} ({2}): {3}\n'.format(timestamp, task, os.getpid(), logbase))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500624 except OSError:
625 logger.exception("Opening log file '%s'", logorder)
626 pass
627
628 # Setup the courtesy link to the logfn
629 loglink = os.path.join(tempdir, 'log.{0}'.format(task))
630 logfn = os.path.join(tempdir, logbase)
631 if loglink:
632 bb.utils.remove(loglink)
633
634 try:
635 os.symlink(logbase, loglink)
636 except OSError:
637 pass
638
639 prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
640 postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
641
642 class ErrorCheckHandler(logging.Handler):
643 def __init__(self):
644 self.triggered = False
645 logging.Handler.__init__(self, logging.ERROR)
646 def emit(self, record):
647 if getattr(record, 'forcelog', False):
648 self.triggered = False
649 else:
650 self.triggered = True
651
652 # Handle logfiles
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500653 try:
654 bb.utils.mkdirhier(os.path.dirname(logfn))
655 logfile = open(logfn, 'w')
656 except OSError:
657 logger.exception("Opening log file '%s'", logfn)
658 pass
659
660 # Dup the existing fds so we dont lose them
661 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
662 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
663 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
664
665 # Replace those fds with our own
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800666 with open('/dev/null', 'r') as si:
667 os.dup2(si.fileno(), osi[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500668 os.dup2(logfile.fileno(), oso[1])
669 os.dup2(logfile.fileno(), ose[1])
670
671 # Ensure Python logging goes to the logfile
672 handler = logging.StreamHandler(logfile)
673 handler.setFormatter(logformatter)
674 # Always enable full debug output into task logfiles
675 handler.setLevel(logging.DEBUG - 2)
676 bblogger.addHandler(handler)
677
678 errchk = ErrorCheckHandler()
679 bblogger.addHandler(errchk)
680
681 localdata.setVar('BB_LOGFILE', logfn)
682 localdata.setVar('BB_RUNTASK', task)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500683 localdata.setVar('BB_TASK_LOGGER', bblogger)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500684
685 flags = localdata.getVarFlags(task)
686
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500687 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600688 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500689 event.fire(TaskStarted(task, fn, logfn, flags, localdata), localdata)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600690
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600691 for func in (prefuncs or '').split():
692 exec_func(func, localdata)
693 exec_func(task, localdata)
694 for func in (postfuncs or '').split():
695 exec_func(func, localdata)
Andrew Geissler5199d832021-09-24 16:47:35 -0500696 finally:
697 # Need to flush and close the logs before sending events where the
698 # UI may try to look at the logs.
699 sys.stdout.flush()
700 sys.stderr.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500701
Andrew Geissler5199d832021-09-24 16:47:35 -0500702 bblogger.removeHandler(handler)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500703
Andrew Geissler5199d832021-09-24 16:47:35 -0500704 # Restore the backup fds
705 os.dup2(osi[0], osi[1])
706 os.dup2(oso[0], oso[1])
707 os.dup2(ose[0], ose[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500708
Andrew Geissler5199d832021-09-24 16:47:35 -0500709 # Close the backup fds
710 os.close(osi[0])
711 os.close(oso[0])
712 os.close(ose[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500713
Andrew Geissler5199d832021-09-24 16:47:35 -0500714 logfile.close()
715 if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
716 logger.debug2("Zero size logfn %s, removing", logfn)
717 bb.utils.remove(logfn)
718 bb.utils.remove(loglink)
Andrew Geissler5199d832021-09-24 16:47:35 -0500719 except (Exception, SystemExit) as exc:
Andrew Geissler595f6302022-01-24 19:11:47 +0000720 handled = False
721 if isinstance(exc, bb.BBHandledException):
722 handled = True
723
Andrew Geissler5199d832021-09-24 16:47:35 -0500724 if quieterr:
Andrew Geissler595f6302022-01-24 19:11:47 +0000725 if not handled:
726 logger.warning(repr(exc))
Andrew Geissler5199d832021-09-24 16:47:35 -0500727 event.fire(TaskFailedSilent(task, fn, logfn, localdata), localdata)
728 else:
729 errprinted = errchk.triggered
730 # If the output is already on stdout, we've printed the information in the
731 # logs once already so don't duplicate
Andrew Geissler595f6302022-01-24 19:11:47 +0000732 if verboseStdoutLogging or handled:
Andrew Geissler5199d832021-09-24 16:47:35 -0500733 errprinted = True
Andrew Geissler595f6302022-01-24 19:11:47 +0000734 if not handled:
735 logger.error(repr(exc))
Andrew Geissler5199d832021-09-24 16:47:35 -0500736 event.fire(TaskFailed(task, fn, logfn, localdata, errprinted), localdata)
737 return 1
738
Andrew Geissler82c905d2020-04-13 13:39:40 -0500739 event.fire(TaskSucceeded(task, fn, logfn, localdata), localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500740
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500741 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500742 make_stamp(task, localdata)
743
744 return 0
745
746def exec_task(fn, task, d, profile = False):
747 try:
748 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500749 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500750 quieterr = True
751
752 if profile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500753 profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500754 try:
755 import cProfile as profile
756 except:
757 import profile
758 prof = profile.Profile()
759 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
760 prof.dump_stats(profname)
761 bb.utils.process_profilelog(profname)
762
763 return ret
764 else:
765 return _exec_task(fn, task, d, quieterr)
766
767 except Exception:
768 from traceback import format_exc
769 if not quieterr:
770 logger.error("Build of %s failed" % (task))
771 logger.error(format_exc())
772 failedevent = TaskFailed(task, None, d, True)
773 event.fire(failedevent, d)
774 return 1
775
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600776def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500777 """
778 Internal stamp helper function
779 Makes sure the stamp directory exists
780 Returns the stamp path+filename
781
782 In the bitbake core, d can be a CacheData and file_name will be set.
783 When called in task context, d will be a data store, file_name will not be set
784 """
785 taskflagname = taskname
786 if taskname.endswith("_setscene") and taskname != "do_setscene":
787 taskflagname = taskname.replace("_setscene", "")
788
789 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500790 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
792 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500793 stamp = d.getVar('STAMP')
794 file_name = d.getVar('BB_FILENAME')
795 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500796
797 if baseonly:
798 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600799 if noextra:
800 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500801
802 if not stamp:
803 return
804
805 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
806
807 stampdir = os.path.dirname(stamp)
808 if cached_mtime_noerror(stampdir) == 0:
809 bb.utils.mkdirhier(stampdir)
810
811 return stamp
812
813def stamp_cleanmask_internal(taskname, d, file_name):
814 """
815 Internal stamp helper function to generate stamp cleaning mask
816 Returns the stamp path+filename
817
818 In the bitbake core, d can be a CacheData and file_name will be set.
819 When called in task context, d will be a data store, file_name will not be set
820 """
821 taskflagname = taskname
822 if taskname.endswith("_setscene") and taskname != "do_setscene":
823 taskflagname = taskname.replace("_setscene", "")
824
825 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500826 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500827 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
828 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500829 stamp = d.getVar('STAMPCLEAN')
830 file_name = d.getVar('BB_FILENAME')
831 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500832
833 if not stamp:
834 return []
835
836 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
837
838 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
839
Andrew Geisslerd5838332022-05-27 11:33:10 -0500840def clean_stamp(task, d, file_name = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500841 cleanmask = stamp_cleanmask_internal(task, d, file_name)
842 for mask in cleanmask:
843 for name in glob.glob(mask):
844 # Preserve sigdata files in the stamps directory
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500845 if "sigdata" in name or "sigbasedata" in name:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500846 continue
847 # Preserve taint files in the stamps directory
848 if name.endswith('.taint'):
849 continue
850 os.unlink(name)
Andrew Geisslerd5838332022-05-27 11:33:10 -0500851 return
852
853def make_stamp(task, d, file_name = None):
854 """
855 Creates/updates a stamp for a given task
856 (d can be a data dict or dataCache)
857 """
858 clean_stamp(task, d, file_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500859
860 stamp = stamp_internal(task, d, file_name)
861 # Remove the file and recreate to force timestamp
862 # change on broken NFS filesystems
863 if stamp:
864 bb.utils.remove(stamp)
865 open(stamp, "w").close()
866
867 # If we're in task context, write out a signature file for each task
868 # as it completes
869 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
870 stampbase = stamp_internal(task, d, None, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500871 file_name = d.getVar('BB_FILENAME')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500872 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
873
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500874def find_stale_stamps(task, d, file_name=None):
875 current = stamp_internal(task, d, file_name)
876 current2 = stamp_internal(task + "_setscene", d, file_name)
877 cleanmask = stamp_cleanmask_internal(task, d, file_name)
878 found = []
879 for mask in cleanmask:
880 for name in glob.glob(mask):
881 if "sigdata" in name or "sigbasedata" in name:
882 continue
883 if name.endswith('.taint'):
884 continue
885 if name == current or name == current2:
886 continue
887 logger.debug2("Stampfile %s does not match %s or %s" % (name, current, current2))
888 found.append(name)
889 return found
890
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500891def del_stamp(task, d, file_name = None):
892 """
893 Removes a stamp for a given task
894 (d can be a data dict or dataCache)
895 """
896 stamp = stamp_internal(task, d, file_name)
897 bb.utils.remove(stamp)
898
899def write_taint(task, d, file_name = None):
900 """
901 Creates a "taint" file which will force the specified task and its
902 dependents to be re-run the next time by influencing the value of its
903 taskhash.
904 (d can be a data dict or dataCache)
905 """
906 import uuid
907 if file_name:
908 taintfn = d.stamp[file_name] + '.' + task + '.taint'
909 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500910 taintfn = d.getVar('STAMP') + '.' + task + '.taint'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500911 bb.utils.mkdirhier(os.path.dirname(taintfn))
912 # The specific content of the taint file is not really important,
913 # we just need it to be random, so a random UUID is used
914 with open(taintfn, 'w') as taintf:
915 taintf.write(str(uuid.uuid4()))
916
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600917def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500918 """
919 Return the stamp for a given task
920 (d can be a data dict or dataCache)
921 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600922 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500923
924def add_tasks(tasklist, d):
925 task_deps = d.getVar('_task_deps', False)
926 if not task_deps:
927 task_deps = {}
928 if not 'tasks' in task_deps:
929 task_deps['tasks'] = []
930 if not 'parents' in task_deps:
931 task_deps['parents'] = {}
932
933 for task in tasklist:
934 task = d.expand(task)
935
936 d.setVarFlag(task, 'task', 1)
937
938 if not task in task_deps['tasks']:
939 task_deps['tasks'].append(task)
940
941 flags = d.getVarFlags(task)
942 def getTask(name):
943 if not name in task_deps:
944 task_deps[name] = {}
945 if name in flags:
946 deptask = d.expand(flags[name])
Andrew Geissler09036742021-06-25 14:25:14 -0500947 if name in ['noexec', 'fakeroot', 'nostamp']:
948 if deptask != '1':
949 bb.warn("In a future version of BitBake, setting the '{}' flag to something other than '1' "
950 "will result in the flag not being set. See YP bug #13808.".format(name))
951
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500952 task_deps[name][task] = deptask
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800953 getTask('mcdepends')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500954 getTask('depends')
955 getTask('rdepends')
956 getTask('deptask')
957 getTask('rdeptask')
958 getTask('recrdeptask')
959 getTask('recideptask')
960 getTask('nostamp')
961 getTask('fakeroot')
962 getTask('noexec')
963 getTask('umask')
964 task_deps['parents'][task] = []
965 if 'deps' in flags:
966 for dep in flags['deps']:
Brad Bishopc342db32019-05-15 21:57:59 -0400967 # Check and warn for "addtask task after foo" while foo does not exist
968 #if not dep in tasklist:
969 # bb.warn('%s: dependent task %s for %s does not exist' % (d.getVar('PN'), dep, task))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500970 dep = d.expand(dep)
971 task_deps['parents'][task].append(dep)
972
973 # don't assume holding a reference
974 d.setVar('_task_deps', task_deps)
975
976def addtask(task, before, after, d):
977 if task[:3] != "do_":
978 task = "do_" + task
979
980 d.setVarFlag(task, "task", 1)
981 bbtasks = d.getVar('__BBTASKS', False) or []
982 if task not in bbtasks:
983 bbtasks.append(task)
984 d.setVar('__BBTASKS', bbtasks)
985
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500986 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500987 if after is not None:
988 # set up deps for function
989 for entry in after.split():
990 if entry not in existing:
991 existing.append(entry)
992 d.setVarFlag(task, "deps", existing)
993 if before is not None:
994 # set up things that depend on this func
995 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500996 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500997 if task not in existing:
998 d.setVarFlag(entry, "deps", [task] + existing)
999
1000def deltask(task, d):
1001 if task[:3] != "do_":
1002 task = "do_" + task
1003
1004 bbtasks = d.getVar('__BBTASKS', False) or []
1005 if task in bbtasks:
1006 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001007 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001008 d.setVar('__BBTASKS', bbtasks)
1009
1010 d.delVarFlag(task, 'deps')
1011 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001012 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001013 if task in deps:
1014 deps.remove(task)
1015 d.setVarFlag(bbtask, 'deps', deps)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001016
1017def preceedtask(task, with_recrdeptasks, d):
1018 """
1019 Returns a set of tasks in the current recipe which were specified as
1020 precondition by the task itself ("after") or which listed themselves
1021 as precondition ("before"). Preceeding tasks specified via the
1022 "recrdeptask" are included in the result only if requested. Beware
1023 that this may lead to the task itself being listed.
1024 """
1025 preceed = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -04001026
1027 # Ignore tasks which don't exist
1028 tasks = d.getVar('__BBTASKS', False)
1029 if task not in tasks:
1030 return preceed
1031
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001032 preceed.update(d.getVarFlag(task, 'deps') or [])
1033 if with_recrdeptasks:
1034 recrdeptask = d.getVarFlag(task, 'recrdeptask')
1035 if recrdeptask:
1036 preceed.update(recrdeptask.split())
1037 return preceed
1038
1039def tasksbetween(task_start, task_end, d):
1040 """
1041 Return the list of tasks between two tasks in the current recipe,
1042 where task_start is to start at and task_end is the task to end at
1043 (and task_end has a dependency chain back to task_start).
1044 """
1045 outtasks = []
1046 tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
1047 def follow_chain(task, endtask, chain=None):
1048 if not chain:
1049 chain = []
Andrew Geissler5199d832021-09-24 16:47:35 -05001050 if task in chain:
1051 bb.fatal("Circular task dependencies as %s depends on itself via the chain %s" % (task, " -> ".join(chain)))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001052 chain.append(task)
1053 for othertask in tasks:
1054 if othertask == task:
1055 continue
1056 if task == endtask:
1057 for ctask in chain:
1058 if ctask not in outtasks:
1059 outtasks.append(ctask)
1060 else:
1061 deps = d.getVarFlag(othertask, 'deps', False)
1062 if task in deps:
1063 follow_chain(othertask, endtask, chain)
1064 chain.pop()
1065 follow_chain(task_start, task_end)
1066 return outtasks