blob: 7e4ab9f64cc346702622e6e67ef241d63b2e9d4a [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)
572 localdata.setVar('BB_CURRENTTASK', task[3:])
573 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):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500586 event.fire(TaskInvalid(task, d), d)
587 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:
622 logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
623 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)
718 except bb.BBHandledException:
719 event.fire(TaskFailed(task, fn, logfn, localdata, True), localdata)
720 return 1
721 except (Exception, SystemExit) as exc:
722 if quieterr:
723 event.fire(TaskFailedSilent(task, fn, logfn, localdata), localdata)
724 else:
725 errprinted = errchk.triggered
726 # If the output is already on stdout, we've printed the information in the
727 # logs once already so don't duplicate
728 if verboseStdoutLogging:
729 errprinted = True
730 logger.error(repr(exc))
731 event.fire(TaskFailed(task, fn, logfn, localdata, errprinted), localdata)
732 return 1
733
Andrew Geissler82c905d2020-04-13 13:39:40 -0500734 event.fire(TaskSucceeded(task, fn, logfn, localdata), localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500735
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500736 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500737 make_stamp(task, localdata)
738
739 return 0
740
741def exec_task(fn, task, d, profile = False):
742 try:
743 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500744 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500745 quieterr = True
746
747 if profile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500748 profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749 try:
750 import cProfile as profile
751 except:
752 import profile
753 prof = profile.Profile()
754 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
755 prof.dump_stats(profname)
756 bb.utils.process_profilelog(profname)
757
758 return ret
759 else:
760 return _exec_task(fn, task, d, quieterr)
761
762 except Exception:
763 from traceback import format_exc
764 if not quieterr:
765 logger.error("Build of %s failed" % (task))
766 logger.error(format_exc())
767 failedevent = TaskFailed(task, None, d, True)
768 event.fire(failedevent, d)
769 return 1
770
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600771def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500772 """
773 Internal stamp helper function
774 Makes sure the stamp directory exists
775 Returns the stamp path+filename
776
777 In the bitbake core, d can be a CacheData and file_name will be set.
778 When called in task context, d will be a data store, file_name will not be set
779 """
780 taskflagname = taskname
781 if taskname.endswith("_setscene") and taskname != "do_setscene":
782 taskflagname = taskname.replace("_setscene", "")
783
784 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500785 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500786 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
787 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500788 stamp = d.getVar('STAMP')
789 file_name = d.getVar('BB_FILENAME')
790 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791
792 if baseonly:
793 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600794 if noextra:
795 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500796
797 if not stamp:
798 return
799
800 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
801
802 stampdir = os.path.dirname(stamp)
803 if cached_mtime_noerror(stampdir) == 0:
804 bb.utils.mkdirhier(stampdir)
805
806 return stamp
807
808def stamp_cleanmask_internal(taskname, d, file_name):
809 """
810 Internal stamp helper function to generate stamp cleaning mask
811 Returns the stamp path+filename
812
813 In the bitbake core, d can be a CacheData and file_name will be set.
814 When called in task context, d will be a data store, file_name will not be set
815 """
816 taskflagname = taskname
817 if taskname.endswith("_setscene") and taskname != "do_setscene":
818 taskflagname = taskname.replace("_setscene", "")
819
820 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500821 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500822 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
823 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500824 stamp = d.getVar('STAMPCLEAN')
825 file_name = d.getVar('BB_FILENAME')
826 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500827
828 if not stamp:
829 return []
830
831 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
832
833 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
834
835def make_stamp(task, d, file_name = None):
836 """
837 Creates/updates a stamp for a given task
838 (d can be a data dict or dataCache)
839 """
840 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)
850
851 stamp = stamp_internal(task, d, file_name)
852 # Remove the file and recreate to force timestamp
853 # change on broken NFS filesystems
854 if stamp:
855 bb.utils.remove(stamp)
856 open(stamp, "w").close()
857
858 # If we're in task context, write out a signature file for each task
859 # as it completes
860 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
861 stampbase = stamp_internal(task, d, None, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500862 file_name = d.getVar('BB_FILENAME')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500863 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
864
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500865def find_stale_stamps(task, d, file_name=None):
866 current = stamp_internal(task, d, file_name)
867 current2 = stamp_internal(task + "_setscene", d, file_name)
868 cleanmask = stamp_cleanmask_internal(task, d, file_name)
869 found = []
870 for mask in cleanmask:
871 for name in glob.glob(mask):
872 if "sigdata" in name or "sigbasedata" in name:
873 continue
874 if name.endswith('.taint'):
875 continue
876 if name == current or name == current2:
877 continue
878 logger.debug2("Stampfile %s does not match %s or %s" % (name, current, current2))
879 found.append(name)
880 return found
881
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500882def del_stamp(task, d, file_name = None):
883 """
884 Removes a stamp for a given task
885 (d can be a data dict or dataCache)
886 """
887 stamp = stamp_internal(task, d, file_name)
888 bb.utils.remove(stamp)
889
890def write_taint(task, d, file_name = None):
891 """
892 Creates a "taint" file which will force the specified task and its
893 dependents to be re-run the next time by influencing the value of its
894 taskhash.
895 (d can be a data dict or dataCache)
896 """
897 import uuid
898 if file_name:
899 taintfn = d.stamp[file_name] + '.' + task + '.taint'
900 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500901 taintfn = d.getVar('STAMP') + '.' + task + '.taint'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500902 bb.utils.mkdirhier(os.path.dirname(taintfn))
903 # The specific content of the taint file is not really important,
904 # we just need it to be random, so a random UUID is used
905 with open(taintfn, 'w') as taintf:
906 taintf.write(str(uuid.uuid4()))
907
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600908def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500909 """
910 Return the stamp for a given task
911 (d can be a data dict or dataCache)
912 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600913 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500914
915def add_tasks(tasklist, d):
916 task_deps = d.getVar('_task_deps', False)
917 if not task_deps:
918 task_deps = {}
919 if not 'tasks' in task_deps:
920 task_deps['tasks'] = []
921 if not 'parents' in task_deps:
922 task_deps['parents'] = {}
923
924 for task in tasklist:
925 task = d.expand(task)
926
927 d.setVarFlag(task, 'task', 1)
928
929 if not task in task_deps['tasks']:
930 task_deps['tasks'].append(task)
931
932 flags = d.getVarFlags(task)
933 def getTask(name):
934 if not name in task_deps:
935 task_deps[name] = {}
936 if name in flags:
937 deptask = d.expand(flags[name])
Andrew Geissler09036742021-06-25 14:25:14 -0500938 if name in ['noexec', 'fakeroot', 'nostamp']:
939 if deptask != '1':
940 bb.warn("In a future version of BitBake, setting the '{}' flag to something other than '1' "
941 "will result in the flag not being set. See YP bug #13808.".format(name))
942
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500943 task_deps[name][task] = deptask
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800944 getTask('mcdepends')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500945 getTask('depends')
946 getTask('rdepends')
947 getTask('deptask')
948 getTask('rdeptask')
949 getTask('recrdeptask')
950 getTask('recideptask')
951 getTask('nostamp')
952 getTask('fakeroot')
953 getTask('noexec')
954 getTask('umask')
955 task_deps['parents'][task] = []
956 if 'deps' in flags:
957 for dep in flags['deps']:
Brad Bishopc342db32019-05-15 21:57:59 -0400958 # Check and warn for "addtask task after foo" while foo does not exist
959 #if not dep in tasklist:
960 # bb.warn('%s: dependent task %s for %s does not exist' % (d.getVar('PN'), dep, task))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500961 dep = d.expand(dep)
962 task_deps['parents'][task].append(dep)
963
964 # don't assume holding a reference
965 d.setVar('_task_deps', task_deps)
966
967def addtask(task, before, after, d):
968 if task[:3] != "do_":
969 task = "do_" + task
970
971 d.setVarFlag(task, "task", 1)
972 bbtasks = d.getVar('__BBTASKS', False) or []
973 if task not in bbtasks:
974 bbtasks.append(task)
975 d.setVar('__BBTASKS', bbtasks)
976
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500977 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500978 if after is not None:
979 # set up deps for function
980 for entry in after.split():
981 if entry not in existing:
982 existing.append(entry)
983 d.setVarFlag(task, "deps", existing)
984 if before is not None:
985 # set up things that depend on this func
986 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500987 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500988 if task not in existing:
989 d.setVarFlag(entry, "deps", [task] + existing)
990
991def deltask(task, d):
992 if task[:3] != "do_":
993 task = "do_" + task
994
995 bbtasks = d.getVar('__BBTASKS', False) or []
996 if task in bbtasks:
997 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600998 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500999 d.setVar('__BBTASKS', bbtasks)
1000
1001 d.delVarFlag(task, 'deps')
1002 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001003 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001004 if task in deps:
1005 deps.remove(task)
1006 d.setVarFlag(bbtask, 'deps', deps)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001007
1008def preceedtask(task, with_recrdeptasks, d):
1009 """
1010 Returns a set of tasks in the current recipe which were specified as
1011 precondition by the task itself ("after") or which listed themselves
1012 as precondition ("before"). Preceeding tasks specified via the
1013 "recrdeptask" are included in the result only if requested. Beware
1014 that this may lead to the task itself being listed.
1015 """
1016 preceed = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -04001017
1018 # Ignore tasks which don't exist
1019 tasks = d.getVar('__BBTASKS', False)
1020 if task not in tasks:
1021 return preceed
1022
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001023 preceed.update(d.getVarFlag(task, 'deps') or [])
1024 if with_recrdeptasks:
1025 recrdeptask = d.getVarFlag(task, 'recrdeptask')
1026 if recrdeptask:
1027 preceed.update(recrdeptask.split())
1028 return preceed
1029
1030def tasksbetween(task_start, task_end, d):
1031 """
1032 Return the list of tasks between two tasks in the current recipe,
1033 where task_start is to start at and task_end is the task to end at
1034 (and task_end has a dependency chain back to task_start).
1035 """
1036 outtasks = []
1037 tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
1038 def follow_chain(task, endtask, chain=None):
1039 if not chain:
1040 chain = []
Andrew Geissler5199d832021-09-24 16:47:35 -05001041 if task in chain:
1042 bb.fatal("Circular task dependencies as %s depends on itself via the chain %s" % (task, " -> ".join(chain)))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001043 chain.append(task)
1044 for othertask in tasks:
1045 if othertask == task:
1046 continue
1047 if task == endtask:
1048 for ctask in chain:
1049 if ctask not in outtasks:
1050 outtasks.append(ctask)
1051 else:
1052 deps = d.getVarFlag(othertask, 'deps', False)
1053 if task in deps:
1054 follow_chain(othertask, endtask, chain)
1055 chain.pop()
1056 follow_chain(task_start, task_end)
1057 return outtasks