blob: d6418e40b30472f556cee9d3b17d0f96b966bc03 [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
Andrew Geisslerc9f78652020-09-18 14:11:35 -050032verboseShellLogging = False
33verboseStdoutLogging = False
34
Patrick Williamsc124f4f2015-09-15 14:41:29 -050035__mtime_cache = {}
36
37def cached_mtime_noerror(f):
38 if f not in __mtime_cache:
39 try:
40 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
41 except OSError:
42 return 0
43 return __mtime_cache[f]
44
45def reset_cache():
46 global __mtime_cache
47 __mtime_cache = {}
48
49# When we execute a Python function, we'd like certain things
50# in all namespaces, hence we add them to __builtins__.
51# If we do not do this and use the exec globals, they will
52# not be available to subfunctions.
Patrick Williamsc0f7c042017-02-23 20:41:17 -060053if hasattr(__builtins__, '__setitem__'):
54 builtins = __builtins__
55else:
56 builtins = __builtins__.__dict__
57
58builtins['bb'] = bb
59builtins['os'] = os
Patrick Williamsc124f4f2015-09-15 14:41:29 -050060
Patrick Williamsc124f4f2015-09-15 14:41:29 -050061class TaskBase(event.Event):
62 """Base class for task events"""
63
Andrew Geissler82c905d2020-04-13 13:39:40 -050064 def __init__(self, t, fn, logfile, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050065 self._task = t
Andrew Geissler82c905d2020-04-13 13:39:40 -050066 self._fn = fn
Brad Bishop6e60e8b2018-02-01 10:27:11 -050067 self._package = d.getVar("PF")
68 self._mc = d.getVar("BB_CURRENT_MC")
69 self.taskfile = d.getVar("FILE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050070 self.taskname = self._task
71 self.logfile = logfile
72 self.time = time.time()
Andrew Geissler82c905d2020-04-13 13:39:40 -050073 self.pn = d.getVar("PN")
74 self.pv = d.getVar("PV")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050075 event.Event.__init__(self)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050076 self._message = "recipe %s: task %s: %s" % (d.getVar("PF"), t, self.getDisplayName())
Patrick Williamsc124f4f2015-09-15 14:41:29 -050077
78 def getTask(self):
79 return self._task
80
81 def setTask(self, task):
82 self._task = task
83
84 def getDisplayName(self):
85 return bb.event.getName(self)[4:]
86
87 task = property(getTask, setTask, None, "task property")
88
89class TaskStarted(TaskBase):
90 """Task execution started"""
Andrew Geissler82c905d2020-04-13 13:39:40 -050091 def __init__(self, t, fn, logfile, taskflags, d):
92 super(TaskStarted, self).__init__(t, fn, logfile, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050093 self.taskflags = taskflags
94
95class TaskSucceeded(TaskBase):
96 """Task execution completed"""
97
98class TaskFailed(TaskBase):
99 """Task execution failed"""
100
Andrew Geissler82c905d2020-04-13 13:39:40 -0500101 def __init__(self, task, fn, logfile, metadata, errprinted = False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102 self.errprinted = errprinted
Andrew Geissler82c905d2020-04-13 13:39:40 -0500103 super(TaskFailed, self).__init__(task, fn, logfile, metadata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500104
105class TaskFailedSilent(TaskBase):
106 """Task execution failed (silently)"""
107 def getDisplayName(self):
108 # Don't need to tell the user it was silent
109 return "Failed"
110
111class TaskInvalid(TaskBase):
112
Andrew Geissler82c905d2020-04-13 13:39:40 -0500113 def __init__(self, task, fn, metadata):
114 super(TaskInvalid, self).__init__(task, fn, None, metadata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500115 self._message = "No such task '%s'" % task
116
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600117class TaskProgress(event.Event):
118 """
119 Task made some progress that could be reported to the user, usually in
120 the form of a progress bar or similar.
121 NOTE: this class does not inherit from TaskBase since it doesn't need
122 to - it's fired within the task context itself, so we don't have any of
123 the context information that you do in the case of the other events.
124 The event PID can be used to determine which task it came from.
125 The progress value is normally 0-100, but can also be negative
126 indicating that progress has been made but we aren't able to determine
127 how much.
128 The rate is optional, this is simply an extra string to display to the
129 user if specified.
130 """
131 def __init__(self, progress, rate=None):
132 self.progress = progress
133 self.rate = rate
134 event.Event.__init__(self)
135
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500136
137class LogTee(object):
138 def __init__(self, logger, outfile):
139 self.outfile = outfile
140 self.logger = logger
141 self.name = self.outfile.name
142
143 def write(self, string):
144 self.logger.plain(string)
145 self.outfile.write(string)
146
147 def __enter__(self):
148 self.outfile.__enter__()
149 return self
150
151 def __exit__(self, *excinfo):
152 self.outfile.__exit__(*excinfo)
153
154 def __repr__(self):
155 return '<LogTee {0}>'.format(self.name)
Brad Bishop15ae2502019-06-18 21:44:24 -0400156
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500157 def flush(self):
158 self.outfile.flush()
159
Brad Bishop15ae2502019-06-18 21:44:24 -0400160
161class StdoutNoopContextManager:
162 """
163 This class acts like sys.stdout, but adds noop __enter__ and __exit__ methods.
164 """
165 def __enter__(self):
166 return sys.stdout
167
168 def __exit__(self, *exc_info):
169 pass
170
171 def write(self, string):
172 return sys.stdout.write(string)
173
174 def flush(self):
175 sys.stdout.flush()
176
177 @property
178 def name(self):
179 return sys.stdout.name
180
181
Brad Bishop08902b02019-08-20 09:16:51 -0400182def exec_func(func, d, dirs = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500183 """Execute a BB 'function'"""
184
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600185 try:
186 oldcwd = os.getcwd()
187 except:
188 oldcwd = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500189
190 flags = d.getVarFlags(func)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500191 cleandirs = flags.get('cleandirs') if flags else None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500192 if cleandirs:
193 for cdir in d.expand(cleandirs).split():
194 bb.utils.remove(cdir, True)
195 bb.utils.mkdirhier(cdir)
196
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500197 if flags and dirs is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500198 dirs = flags.get('dirs')
199 if dirs:
200 dirs = d.expand(dirs).split()
201
202 if dirs:
203 for adir in dirs:
204 bb.utils.mkdirhier(adir)
205 adir = dirs[-1]
206 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600207 adir = None
208
209 body = d.getVar(func, False)
210 if not body:
211 if body is None:
212 logger.warning("Function %s doesn't exist", func)
213 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500214
215 ispython = flags.get('python')
216
217 lockflag = flags.get('lockfiles')
218 if lockflag:
219 lockfiles = [f for f in d.expand(lockflag).split()]
220 else:
221 lockfiles = None
222
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500223 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500224
225 # or func allows items to be executed outside of the normal
226 # task set, such as buildhistory
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500227 task = d.getVar('BB_RUNTASK') or func
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500228 if task == func:
229 taskfunc = task
230 else:
231 taskfunc = "%s.%s" % (task, func)
232
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500233 runfmt = d.getVar('BB_RUNFMT') or "run.{func}.{pid}"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500234 runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
235 runfile = os.path.join(tempdir, runfn)
236 bb.utils.mkdirhier(os.path.dirname(runfile))
237
238 # Setup the courtesy link to the runfn, only for tasks
239 # we create the link 'just' before the run script is created
240 # if we create it after, and if the run script fails, then the
241 # link won't be created as an exception would be fired.
242 if task == func:
243 runlink = os.path.join(tempdir, 'run.{0}'.format(task))
244 if runlink:
245 bb.utils.remove(runlink)
246
247 try:
248 os.symlink(runfn, runlink)
249 except OSError:
250 pass
251
252 with bb.utils.fileslocked(lockfiles):
253 if ispython:
Brad Bishop08902b02019-08-20 09:16:51 -0400254 exec_func_python(func, d, runfile, cwd=adir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500255 else:
256 exec_func_shell(func, d, runfile, cwd=adir)
257
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600258 try:
259 curcwd = os.getcwd()
260 except:
261 curcwd = None
262
263 if oldcwd and curcwd != oldcwd:
264 try:
265 bb.warn("Task %s changed cwd to %s" % (func, curcwd))
266 os.chdir(oldcwd)
267 except:
268 pass
269
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500270_functionfmt = """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271{function}(d)
272"""
273logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
Brad Bishop08902b02019-08-20 09:16:51 -0400274def exec_func_python(func, d, runfile, cwd=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500275 """Execute a python BB 'function'"""
276
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500277 code = _functionfmt.format(function=func)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500278 bb.utils.mkdirhier(os.path.dirname(runfile))
279 with open(runfile, 'w') as script:
280 bb.data.emit_func_python(func, script, d)
281
282 if cwd:
283 try:
284 olddir = os.getcwd()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600285 except OSError as e:
286 bb.warn("%s: Cannot get cwd: %s" % (func, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500287 olddir = None
288 os.chdir(cwd)
289
290 bb.debug(2, "Executing python function %s" % func)
291
292 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500293 text = "def %s(d):\n%s" % (func, d.getVar(func, False))
294 fn = d.getVarFlag(func, "filename", False)
295 lineno = int(d.getVarFlag(func, "lineno", False))
296 bb.methodpool.insert_method(func, text, fn, lineno - 1)
297
Andrew Geissler5199d832021-09-24 16:47:35 -0500298 comp = utils.better_compile(code, func, "exec_func_python() autogenerated")
299 utils.better_exec(comp, {"d": d}, code, "exec_func_python() autogenerated")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500300 finally:
Andrew Geissler5199d832021-09-24 16:47:35 -0500301 # We want any stdout/stderr to be printed before any other log messages to make debugging
302 # more accurate. In some cases we seem to lose stdout/stderr entirely in logging tests without this.
303 sys.stdout.flush()
304 sys.stderr.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500305 bb.debug(2, "Python function %s finished" % func)
306
307 if cwd and olddir:
308 try:
309 os.chdir(olddir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600310 except OSError as e:
311 bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500312
313def shell_trap_code():
314 return '''#!/bin/sh\n
Andrew Geissler635e0e42020-08-21 15:58:33 -0500315__BITBAKE_LAST_LINE=0
316
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317# Emit a useful diagnostic if something fails:
Andrew Geissler635e0e42020-08-21 15:58:33 -0500318bb_sh_exit_handler() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500319 ret=$?
Andrew Geissler635e0e42020-08-21 15:58:33 -0500320 if [ "$ret" != 0 ]; then
321 echo "WARNING: exit code $ret from a shell command."
322 fi
323 exit $ret
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500324}
Andrew Geissler635e0e42020-08-21 15:58:33 -0500325
326bb_bash_exit_handler() {
327 ret=$?
328 { set +x; } > /dev/null
329 trap "" DEBUG
330 if [ "$ret" != 0 ]; then
331 echo "WARNING: ${BASH_SOURCE[0]}:${__BITBAKE_LAST_LINE} exit $ret from '$1'"
332
333 echo "WARNING: Backtrace (BB generated script): "
334 for i in $(seq 1 $((${#FUNCNAME[@]} - 1))); do
335 if [ "$i" -eq 1 ]; then
336 echo -e "\t#$((i)): ${FUNCNAME[$i]}, ${BASH_SOURCE[$((i-1))]}, line ${__BITBAKE_LAST_LINE}"
337 else
338 echo -e "\t#$((i)): ${FUNCNAME[$i]}, ${BASH_SOURCE[$((i-1))]}, line ${BASH_LINENO[$((i-1))]}"
339 fi
340 done
341 fi
342 exit $ret
343}
344
345bb_bash_debug_handler() {
346 local line=${BASH_LINENO[0]}
347 # For some reason the DEBUG trap trips with lineno=1 when scripts exit; ignore it
348 if [ "$line" -eq 1 ]; then
349 return
350 fi
351
352 # Track the line number of commands as they execute. This is so we can have access to the failing line number
353 # 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
354 if [ "${FUNCNAME[1]}" != "bb_bash_exit_handler" ]; then
355 __BITBAKE_LAST_LINE=$line
356 fi
357}
358
359case $BASH_VERSION in
360"") trap 'bb_sh_exit_handler' 0
361 set -e
362 ;;
363*) trap 'bb_bash_exit_handler "$BASH_COMMAND"' 0
364 trap '{ bb_bash_debug_handler; } 2>/dev/null' DEBUG
365 set -e
366 shopt -s extdebug
367 ;;
368esac
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500369'''
370
Brad Bishop15ae2502019-06-18 21:44:24 -0400371def create_progress_handler(func, progress, logfile, d):
372 if progress == 'percent':
373 # Use default regex
374 return bb.progress.BasicProgressHandler(d, outfile=logfile)
375 elif progress.startswith('percent:'):
376 # Use specified regex
377 return bb.progress.BasicProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
378 elif progress.startswith('outof:'):
379 # Use specified regex
380 return bb.progress.OutOfProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
381 elif progress.startswith("custom:"):
382 # Use a custom progress handler that was injected via OE_EXTRA_IMPORTS or __builtins__
383 import functools
384 from types import ModuleType
385
386 parts = progress.split(":", 2)
387 _, cls, otherargs = parts[0], parts[1], (parts[2] or None) if parts[2:] else None
388 if cls:
389 def resolve(x, y):
390 if not x:
391 return None
392 if isinstance(x, ModuleType):
393 return getattr(x, y, None)
394 return x.get(y)
395 cls_obj = functools.reduce(resolve, cls.split("."), bb.utils._context)
396 if not cls_obj:
397 # Fall-back on __builtins__
Andrew Geissler635e0e42020-08-21 15:58:33 -0500398 cls_obj = functools.reduce(resolve, cls.split("."), __builtins__)
Brad Bishop15ae2502019-06-18 21:44:24 -0400399 if cls_obj:
400 return cls_obj(d, outfile=logfile, otherargs=otherargs)
401 bb.warn('%s: unknown custom progress handler in task progress varflag value "%s", ignoring' % (func, cls))
402 else:
403 bb.warn('%s: invalid task progress varflag value "%s", ignoring' % (func, progress))
404
405 return logfile
406
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500407def exec_func_shell(func, d, runfile, cwd=None):
408 """Execute a shell function from the metadata
409
410 Note on directory behavior. The 'dirs' varflag should contain a list
411 of the directories you need created prior to execution. The last
412 item in the list is where we will chdir/cd to.
413 """
414
415 # Don't let the emitted shell script override PWD
416 d.delVarFlag('PWD', 'export')
417
418 with open(runfile, 'w') as script:
419 script.write(shell_trap_code())
420
421 bb.data.emit_func(func, script, d)
422
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500423 if verboseShellLogging or bb.utils.to_boolean(d.getVar("BB_VERBOSE_LOGS", False)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500424 script.write("set -x\n")
425 if cwd:
426 script.write("cd '%s'\n" % cwd)
427 script.write("%s\n" % func)
428 script.write('''
429# cleanup
430ret=$?
431trap '' 0
432exit $ret
433''')
434
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600435 os.chmod(runfile, 0o775)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500436
437 cmd = runfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500438 if d.getVarFlag(func, 'fakeroot', False):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500439 fakerootcmd = d.getVar('FAKEROOT')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500440 if fakerootcmd:
441 cmd = [fakerootcmd, runfile]
442
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500443 if verboseStdoutLogging:
Brad Bishop15ae2502019-06-18 21:44:24 -0400444 logfile = LogTee(logger, StdoutNoopContextManager())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500445 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400446 logfile = StdoutNoopContextManager()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500447
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500448 progress = d.getVarFlag(func, 'progress')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600449 if progress:
Andrew Geissler635e0e42020-08-21 15:58:33 -0500450 try:
451 logfile = create_progress_handler(func, progress, logfile, d)
452 except:
453 from traceback import format_exc
454 logger.error("Failed to create progress handler")
455 logger.error(format_exc())
456 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600457
458 fifobuffer = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500459 def readfifo(data):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600460 nonlocal fifobuffer
461 fifobuffer.extend(data)
462 while fifobuffer:
463 message, token, nextmsg = fifobuffer.partition(b"\00")
464 if token:
465 splitval = message.split(b' ', 1)
466 cmd = splitval[0].decode("utf-8")
467 if len(splitval) > 1:
468 value = splitval[1].decode("utf-8")
469 else:
470 value = ''
471 if cmd == 'bbplain':
472 bb.plain(value)
473 elif cmd == 'bbnote':
474 bb.note(value)
Brad Bishopc342db32019-05-15 21:57:59 -0400475 elif cmd == 'bbverbnote':
476 bb.verbnote(value)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600477 elif cmd == 'bbwarn':
478 bb.warn(value)
479 elif cmd == 'bberror':
480 bb.error(value)
481 elif cmd == 'bbfatal':
482 # The caller will call exit themselves, so bb.error() is
483 # what we want here rather than bb.fatal()
484 bb.error(value)
485 elif cmd == 'bbfatal_log':
486 bb.error(value, forcelog=True)
487 elif cmd == 'bbdebug':
488 splitval = value.split(' ', 1)
489 level = int(splitval[0])
490 value = splitval[1]
491 bb.debug(level, value)
492 else:
493 bb.warn("Unrecognised command '%s' on FIFO" % cmd)
494 fifobuffer = nextmsg
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500495 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600496 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500497
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500498 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500499 fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
500 if os.path.exists(fifopath):
501 os.unlink(fifopath)
502 os.mkfifo(fifopath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600503 with open(fifopath, 'r+b', buffering=0) as fifo:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500504 try:
505 bb.debug(2, "Executing shell function %s" % func)
Brad Bishop08902b02019-08-20 09:16:51 -0400506 with open(os.devnull, 'r+') as stdin, logfile:
507 bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
Andrew Geissler635e0e42020-08-21 15:58:33 -0500508 except bb.process.ExecutionError as exe:
509 # Find the backtrace that the shell trap generated
510 backtrace_marker_regex = re.compile(r"WARNING: Backtrace \(BB generated script\)")
511 stdout_lines = (exe.stdout or "").split("\n")
512 backtrace_start_line = None
513 for i, line in enumerate(reversed(stdout_lines)):
514 if backtrace_marker_regex.search(line):
515 backtrace_start_line = len(stdout_lines) - i
516 break
517
518 # Read the backtrace frames, starting at the location we just found
519 backtrace_entry_regex = re.compile(r"#(?P<frameno>\d+): (?P<funcname>[^\s]+), (?P<file>.+?), line ("
520 r"?P<lineno>\d+)")
521 backtrace_frames = []
522 if backtrace_start_line:
523 for line in itertools.islice(stdout_lines, backtrace_start_line, None):
524 match = backtrace_entry_regex.search(line)
525 if match:
526 backtrace_frames.append(match.groupdict())
527
528 with open(runfile, "r") as script:
529 script_lines = [line.rstrip() for line in script.readlines()]
530
531 # For each backtrace frame, search backwards in the script (from the line number called out by the frame),
532 # to find the comment that emit_vars injected when it wrote the script. This will give us the metadata
533 # filename (e.g. .bb or .bbclass) and line number where the shell function was originally defined.
534 script_metadata_comment_regex = re.compile(r"# line: (?P<lineno>\d+), file: (?P<file>.+)")
535 better_frames = []
536 # Skip the very last frame since it's just the call to the shell task in the body of the script
537 for frame in backtrace_frames[:-1]:
538 # Check whether the frame corresponds to a function defined in the script vs external script.
539 if os.path.samefile(frame["file"], runfile):
540 # Search backwards from the frame lineno to locate the comment that BB injected
541 i = int(frame["lineno"]) - 1
542 while i >= 0:
543 match = script_metadata_comment_regex.match(script_lines[i])
544 if match:
545 # Calculate the relative line in the function itself
546 relative_line_in_function = int(frame["lineno"]) - i - 2
547 # Calculate line in the function as declared in the metadata
548 metadata_function_line = relative_line_in_function + int(match["lineno"])
549 better_frames.append("#{frameno}: {funcname}, {file}, line {lineno}".format(
550 frameno=frame["frameno"],
551 funcname=frame["funcname"],
552 file=match["file"],
553 lineno=metadata_function_line
554 ))
555 break
556 i -= 1
557 else:
558 better_frames.append("#{frameno}: {funcname}, {file}, line {lineno}".format(**frame))
559
560 if better_frames:
561 better_frames = ("\t{0}".format(frame) for frame in better_frames)
562 exe.extra_message = "\nBacktrace (metadata-relative locations):\n{0}".format("\n".join(better_frames))
563 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500564 finally:
565 os.unlink(fifopath)
566
567 bb.debug(2, "Shell function %s finished" % func)
568
569def _task_data(fn, task, d):
570 localdata = bb.data.createCopy(d)
571 localdata.setVar('BB_FILENAME', fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500572 localdata.setVar('OVERRIDES', 'task-%s:%s' %
573 (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
574 localdata.finalize()
575 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):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500585 event.fire(TaskInvalid(task, d), d)
586 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:
621 logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
622 except OSError:
623 logger.exception("Opening log file '%s'", logorder)
624 pass
625
626 # Setup the courtesy link to the logfn
627 loglink = os.path.join(tempdir, 'log.{0}'.format(task))
628 logfn = os.path.join(tempdir, logbase)
629 if loglink:
630 bb.utils.remove(loglink)
631
632 try:
633 os.symlink(logbase, loglink)
634 except OSError:
635 pass
636
637 prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
638 postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
639
640 class ErrorCheckHandler(logging.Handler):
641 def __init__(self):
642 self.triggered = False
643 logging.Handler.__init__(self, logging.ERROR)
644 def emit(self, record):
645 if getattr(record, 'forcelog', False):
646 self.triggered = False
647 else:
648 self.triggered = True
649
650 # Handle logfiles
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 try:
652 bb.utils.mkdirhier(os.path.dirname(logfn))
653 logfile = open(logfn, 'w')
654 except OSError:
655 logger.exception("Opening log file '%s'", logfn)
656 pass
657
658 # Dup the existing fds so we dont lose them
659 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
660 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
661 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
662
663 # Replace those fds with our own
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800664 with open('/dev/null', 'r') as si:
665 os.dup2(si.fileno(), osi[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500666 os.dup2(logfile.fileno(), oso[1])
667 os.dup2(logfile.fileno(), ose[1])
668
669 # Ensure Python logging goes to the logfile
670 handler = logging.StreamHandler(logfile)
671 handler.setFormatter(logformatter)
672 # Always enable full debug output into task logfiles
673 handler.setLevel(logging.DEBUG - 2)
674 bblogger.addHandler(handler)
675
676 errchk = ErrorCheckHandler()
677 bblogger.addHandler(errchk)
678
679 localdata.setVar('BB_LOGFILE', logfn)
680 localdata.setVar('BB_RUNTASK', task)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500681 localdata.setVar('BB_TASK_LOGGER', bblogger)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500682
683 flags = localdata.getVarFlags(task)
684
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500685 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600686 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500687 event.fire(TaskStarted(task, fn, logfn, flags, localdata), localdata)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600688
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600689 for func in (prefuncs or '').split():
690 exec_func(func, localdata)
691 exec_func(task, localdata)
692 for func in (postfuncs or '').split():
693 exec_func(func, localdata)
Andrew Geissler5199d832021-09-24 16:47:35 -0500694 finally:
695 # Need to flush and close the logs before sending events where the
696 # UI may try to look at the logs.
697 sys.stdout.flush()
698 sys.stderr.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500699
Andrew Geissler5199d832021-09-24 16:47:35 -0500700 bblogger.removeHandler(handler)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500701
Andrew Geissler5199d832021-09-24 16:47:35 -0500702 # Restore the backup fds
703 os.dup2(osi[0], osi[1])
704 os.dup2(oso[0], oso[1])
705 os.dup2(ose[0], ose[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500706
Andrew Geissler5199d832021-09-24 16:47:35 -0500707 # Close the backup fds
708 os.close(osi[0])
709 os.close(oso[0])
710 os.close(ose[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500711
Andrew Geissler5199d832021-09-24 16:47:35 -0500712 logfile.close()
713 if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
714 logger.debug2("Zero size logfn %s, removing", logfn)
715 bb.utils.remove(logfn)
716 bb.utils.remove(loglink)
717 except bb.BBHandledException:
718 event.fire(TaskFailed(task, fn, logfn, localdata, True), localdata)
719 return 1
720 except (Exception, SystemExit) as exc:
721 if quieterr:
722 event.fire(TaskFailedSilent(task, fn, logfn, localdata), localdata)
723 else:
724 errprinted = errchk.triggered
725 # If the output is already on stdout, we've printed the information in the
726 # logs once already so don't duplicate
727 if verboseStdoutLogging:
728 errprinted = True
729 logger.error(repr(exc))
730 event.fire(TaskFailed(task, fn, logfn, localdata, errprinted), localdata)
731 return 1
732
Andrew Geissler82c905d2020-04-13 13:39:40 -0500733 event.fire(TaskSucceeded(task, fn, logfn, localdata), localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500734
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500735 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736 make_stamp(task, localdata)
737
738 return 0
739
740def exec_task(fn, task, d, profile = False):
741 try:
742 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500743 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500744 quieterr = True
745
746 if profile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500747 profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500748 try:
749 import cProfile as profile
750 except:
751 import profile
752 prof = profile.Profile()
753 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
754 prof.dump_stats(profname)
755 bb.utils.process_profilelog(profname)
756
757 return ret
758 else:
759 return _exec_task(fn, task, d, quieterr)
760
761 except Exception:
762 from traceback import format_exc
763 if not quieterr:
764 logger.error("Build of %s failed" % (task))
765 logger.error(format_exc())
766 failedevent = TaskFailed(task, None, d, True)
767 event.fire(failedevent, d)
768 return 1
769
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600770def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771 """
772 Internal stamp helper function
773 Makes sure the stamp directory exists
774 Returns the stamp path+filename
775
776 In the bitbake core, d can be a CacheData and file_name will be set.
777 When called in task context, d will be a data store, file_name will not be set
778 """
779 taskflagname = taskname
780 if taskname.endswith("_setscene") and taskname != "do_setscene":
781 taskflagname = taskname.replace("_setscene", "")
782
783 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500784 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500785 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
786 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500787 stamp = d.getVar('STAMP')
788 file_name = d.getVar('BB_FILENAME')
789 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500790
791 if baseonly:
792 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600793 if noextra:
794 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500795
796 if not stamp:
797 return
798
799 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
800
801 stampdir = os.path.dirname(stamp)
802 if cached_mtime_noerror(stampdir) == 0:
803 bb.utils.mkdirhier(stampdir)
804
805 return stamp
806
807def stamp_cleanmask_internal(taskname, d, file_name):
808 """
809 Internal stamp helper function to generate stamp cleaning mask
810 Returns the stamp path+filename
811
812 In the bitbake core, d can be a CacheData and file_name will be set.
813 When called in task context, d will be a data store, file_name will not be set
814 """
815 taskflagname = taskname
816 if taskname.endswith("_setscene") and taskname != "do_setscene":
817 taskflagname = taskname.replace("_setscene", "")
818
819 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500820 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
822 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500823 stamp = d.getVar('STAMPCLEAN')
824 file_name = d.getVar('BB_FILENAME')
825 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500826
827 if not stamp:
828 return []
829
830 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
831
832 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
833
834def make_stamp(task, d, file_name = None):
835 """
836 Creates/updates a stamp for a given task
837 (d can be a data dict or dataCache)
838 """
839 cleanmask = stamp_cleanmask_internal(task, d, file_name)
840 for mask in cleanmask:
841 for name in glob.glob(mask):
842 # Preserve sigdata files in the stamps directory
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500843 if "sigdata" in name or "sigbasedata" in name:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844 continue
845 # Preserve taint files in the stamps directory
846 if name.endswith('.taint'):
847 continue
848 os.unlink(name)
849
850 stamp = stamp_internal(task, d, file_name)
851 # Remove the file and recreate to force timestamp
852 # change on broken NFS filesystems
853 if stamp:
854 bb.utils.remove(stamp)
855 open(stamp, "w").close()
856
857 # If we're in task context, write out a signature file for each task
858 # as it completes
859 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
860 stampbase = stamp_internal(task, d, None, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500861 file_name = d.getVar('BB_FILENAME')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500862 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
863
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500864def find_stale_stamps(task, d, file_name=None):
865 current = stamp_internal(task, d, file_name)
866 current2 = stamp_internal(task + "_setscene", d, file_name)
867 cleanmask = stamp_cleanmask_internal(task, d, file_name)
868 found = []
869 for mask in cleanmask:
870 for name in glob.glob(mask):
871 if "sigdata" in name or "sigbasedata" in name:
872 continue
873 if name.endswith('.taint'):
874 continue
875 if name == current or name == current2:
876 continue
877 logger.debug2("Stampfile %s does not match %s or %s" % (name, current, current2))
878 found.append(name)
879 return found
880
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500881def del_stamp(task, d, file_name = None):
882 """
883 Removes a stamp for a given task
884 (d can be a data dict or dataCache)
885 """
886 stamp = stamp_internal(task, d, file_name)
887 bb.utils.remove(stamp)
888
889def write_taint(task, d, file_name = None):
890 """
891 Creates a "taint" file which will force the specified task and its
892 dependents to be re-run the next time by influencing the value of its
893 taskhash.
894 (d can be a data dict or dataCache)
895 """
896 import uuid
897 if file_name:
898 taintfn = d.stamp[file_name] + '.' + task + '.taint'
899 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500900 taintfn = d.getVar('STAMP') + '.' + task + '.taint'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500901 bb.utils.mkdirhier(os.path.dirname(taintfn))
902 # The specific content of the taint file is not really important,
903 # we just need it to be random, so a random UUID is used
904 with open(taintfn, 'w') as taintf:
905 taintf.write(str(uuid.uuid4()))
906
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600907def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500908 """
909 Return the stamp for a given task
910 (d can be a data dict or dataCache)
911 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600912 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500913
914def add_tasks(tasklist, d):
915 task_deps = d.getVar('_task_deps', False)
916 if not task_deps:
917 task_deps = {}
918 if not 'tasks' in task_deps:
919 task_deps['tasks'] = []
920 if not 'parents' in task_deps:
921 task_deps['parents'] = {}
922
923 for task in tasklist:
924 task = d.expand(task)
925
926 d.setVarFlag(task, 'task', 1)
927
928 if not task in task_deps['tasks']:
929 task_deps['tasks'].append(task)
930
931 flags = d.getVarFlags(task)
932 def getTask(name):
933 if not name in task_deps:
934 task_deps[name] = {}
935 if name in flags:
936 deptask = d.expand(flags[name])
Andrew Geissler09036742021-06-25 14:25:14 -0500937 if name in ['noexec', 'fakeroot', 'nostamp']:
938 if deptask != '1':
939 bb.warn("In a future version of BitBake, setting the '{}' flag to something other than '1' "
940 "will result in the flag not being set. See YP bug #13808.".format(name))
941
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500942 task_deps[name][task] = deptask
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800943 getTask('mcdepends')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500944 getTask('depends')
945 getTask('rdepends')
946 getTask('deptask')
947 getTask('rdeptask')
948 getTask('recrdeptask')
949 getTask('recideptask')
950 getTask('nostamp')
951 getTask('fakeroot')
952 getTask('noexec')
953 getTask('umask')
954 task_deps['parents'][task] = []
955 if 'deps' in flags:
956 for dep in flags['deps']:
Brad Bishopc342db32019-05-15 21:57:59 -0400957 # Check and warn for "addtask task after foo" while foo does not exist
958 #if not dep in tasklist:
959 # bb.warn('%s: dependent task %s for %s does not exist' % (d.getVar('PN'), dep, task))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500960 dep = d.expand(dep)
961 task_deps['parents'][task].append(dep)
962
963 # don't assume holding a reference
964 d.setVar('_task_deps', task_deps)
965
966def addtask(task, before, after, d):
967 if task[:3] != "do_":
968 task = "do_" + task
969
970 d.setVarFlag(task, "task", 1)
971 bbtasks = d.getVar('__BBTASKS', False) or []
972 if task not in bbtasks:
973 bbtasks.append(task)
974 d.setVar('__BBTASKS', bbtasks)
975
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500976 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500977 if after is not None:
978 # set up deps for function
979 for entry in after.split():
980 if entry not in existing:
981 existing.append(entry)
982 d.setVarFlag(task, "deps", existing)
983 if before is not None:
984 # set up things that depend on this func
985 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500986 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500987 if task not in existing:
988 d.setVarFlag(entry, "deps", [task] + existing)
989
990def deltask(task, d):
991 if task[:3] != "do_":
992 task = "do_" + task
993
994 bbtasks = d.getVar('__BBTASKS', False) or []
995 if task in bbtasks:
996 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600997 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500998 d.setVar('__BBTASKS', bbtasks)
999
1000 d.delVarFlag(task, 'deps')
1001 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001002 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001003 if task in deps:
1004 deps.remove(task)
1005 d.setVarFlag(bbtask, 'deps', deps)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001006
1007def preceedtask(task, with_recrdeptasks, d):
1008 """
1009 Returns a set of tasks in the current recipe which were specified as
1010 precondition by the task itself ("after") or which listed themselves
1011 as precondition ("before"). Preceeding tasks specified via the
1012 "recrdeptask" are included in the result only if requested. Beware
1013 that this may lead to the task itself being listed.
1014 """
1015 preceed = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -04001016
1017 # Ignore tasks which don't exist
1018 tasks = d.getVar('__BBTASKS', False)
1019 if task not in tasks:
1020 return preceed
1021
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001022 preceed.update(d.getVarFlag(task, 'deps') or [])
1023 if with_recrdeptasks:
1024 recrdeptask = d.getVarFlag(task, 'recrdeptask')
1025 if recrdeptask:
1026 preceed.update(recrdeptask.split())
1027 return preceed
1028
1029def tasksbetween(task_start, task_end, d):
1030 """
1031 Return the list of tasks between two tasks in the current recipe,
1032 where task_start is to start at and task_end is the task to end at
1033 (and task_end has a dependency chain back to task_start).
1034 """
1035 outtasks = []
1036 tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
1037 def follow_chain(task, endtask, chain=None):
1038 if not chain:
1039 chain = []
Andrew Geissler5199d832021-09-24 16:47:35 -05001040 if task in chain:
1041 bb.fatal("Circular task dependencies as %s depends on itself via the chain %s" % (task, " -> ".join(chain)))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001042 chain.append(task)
1043 for othertask in tasks:
1044 if othertask == task:
1045 continue
1046 if task == endtask:
1047 for ctask in chain:
1048 if ctask not in outtasks:
1049 outtasks.append(ctask)
1050 else:
1051 deps = d.getVarFlag(othertask, 'deps', False)
1052 if task in deps:
1053 follow_chain(othertask, endtask, chain)
1054 chain.pop()
1055 follow_chain(task_start, task_end)
1056 return outtasks