blob: 65b7fc000d3a7ab71e1f564af3234bbae4aaebc0 [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)
Andrew Geissler5199d832021-09-24 16:47:35 -0500717 except (Exception, SystemExit) as exc:
Andrew Geissler595f6302022-01-24 19:11:47 +0000718 handled = False
719 if isinstance(exc, bb.BBHandledException):
720 handled = True
721
Andrew Geissler5199d832021-09-24 16:47:35 -0500722 if quieterr:
Andrew Geissler595f6302022-01-24 19:11:47 +0000723 if not handled:
724 logger.warning(repr(exc))
Andrew Geissler5199d832021-09-24 16:47:35 -0500725 event.fire(TaskFailedSilent(task, fn, logfn, localdata), localdata)
726 else:
727 errprinted = errchk.triggered
728 # If the output is already on stdout, we've printed the information in the
729 # logs once already so don't duplicate
Andrew Geissler595f6302022-01-24 19:11:47 +0000730 if verboseStdoutLogging or handled:
Andrew Geissler5199d832021-09-24 16:47:35 -0500731 errprinted = True
Andrew Geissler595f6302022-01-24 19:11:47 +0000732 if not handled:
733 logger.error(repr(exc))
Andrew Geissler5199d832021-09-24 16:47:35 -0500734 event.fire(TaskFailed(task, fn, logfn, localdata, errprinted), localdata)
735 return 1
736
Andrew Geissler82c905d2020-04-13 13:39:40 -0500737 event.fire(TaskSucceeded(task, fn, logfn, localdata), localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500738
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500739 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500740 make_stamp(task, localdata)
741
742 return 0
743
744def exec_task(fn, task, d, profile = False):
745 try:
746 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500747 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500748 quieterr = True
749
750 if profile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500751 profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500752 try:
753 import cProfile as profile
754 except:
755 import profile
756 prof = profile.Profile()
757 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
758 prof.dump_stats(profname)
759 bb.utils.process_profilelog(profname)
760
761 return ret
762 else:
763 return _exec_task(fn, task, d, quieterr)
764
765 except Exception:
766 from traceback import format_exc
767 if not quieterr:
768 logger.error("Build of %s failed" % (task))
769 logger.error(format_exc())
770 failedevent = TaskFailed(task, None, d, True)
771 event.fire(failedevent, d)
772 return 1
773
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600774def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775 """
776 Internal stamp helper function
777 Makes sure the stamp directory exists
778 Returns the stamp path+filename
779
780 In the bitbake core, d can be a CacheData and file_name will be set.
781 When called in task context, d will be a data store, file_name will not be set
782 """
783 taskflagname = taskname
784 if taskname.endswith("_setscene") and taskname != "do_setscene":
785 taskflagname = taskname.replace("_setscene", "")
786
787 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500788 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500789 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
790 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500791 stamp = d.getVar('STAMP')
792 file_name = d.getVar('BB_FILENAME')
793 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500794
795 if baseonly:
796 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600797 if noextra:
798 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500799
800 if not stamp:
801 return
802
803 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
804
805 stampdir = os.path.dirname(stamp)
806 if cached_mtime_noerror(stampdir) == 0:
807 bb.utils.mkdirhier(stampdir)
808
809 return stamp
810
811def stamp_cleanmask_internal(taskname, d, file_name):
812 """
813 Internal stamp helper function to generate stamp cleaning mask
814 Returns the stamp path+filename
815
816 In the bitbake core, d can be a CacheData and file_name will be set.
817 When called in task context, d will be a data store, file_name will not be set
818 """
819 taskflagname = taskname
820 if taskname.endswith("_setscene") and taskname != "do_setscene":
821 taskflagname = taskname.replace("_setscene", "")
822
823 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500824 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500825 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
826 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500827 stamp = d.getVar('STAMPCLEAN')
828 file_name = d.getVar('BB_FILENAME')
829 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500830
831 if not stamp:
832 return []
833
834 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
835
836 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
837
838def make_stamp(task, d, file_name = None):
839 """
840 Creates/updates a stamp for a given task
841 (d can be a data dict or dataCache)
842 """
843 cleanmask = stamp_cleanmask_internal(task, d, file_name)
844 for mask in cleanmask:
845 for name in glob.glob(mask):
846 # Preserve sigdata files in the stamps directory
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500847 if "sigdata" in name or "sigbasedata" in name:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500848 continue
849 # Preserve taint files in the stamps directory
850 if name.endswith('.taint'):
851 continue
852 os.unlink(name)
853
854 stamp = stamp_internal(task, d, file_name)
855 # Remove the file and recreate to force timestamp
856 # change on broken NFS filesystems
857 if stamp:
858 bb.utils.remove(stamp)
859 open(stamp, "w").close()
860
861 # If we're in task context, write out a signature file for each task
862 # as it completes
863 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
864 stampbase = stamp_internal(task, d, None, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500865 file_name = d.getVar('BB_FILENAME')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500866 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
867
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500868def find_stale_stamps(task, d, file_name=None):
869 current = stamp_internal(task, d, file_name)
870 current2 = stamp_internal(task + "_setscene", d, file_name)
871 cleanmask = stamp_cleanmask_internal(task, d, file_name)
872 found = []
873 for mask in cleanmask:
874 for name in glob.glob(mask):
875 if "sigdata" in name or "sigbasedata" in name:
876 continue
877 if name.endswith('.taint'):
878 continue
879 if name == current or name == current2:
880 continue
881 logger.debug2("Stampfile %s does not match %s or %s" % (name, current, current2))
882 found.append(name)
883 return found
884
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500885def del_stamp(task, d, file_name = None):
886 """
887 Removes a stamp for a given task
888 (d can be a data dict or dataCache)
889 """
890 stamp = stamp_internal(task, d, file_name)
891 bb.utils.remove(stamp)
892
893def write_taint(task, d, file_name = None):
894 """
895 Creates a "taint" file which will force the specified task and its
896 dependents to be re-run the next time by influencing the value of its
897 taskhash.
898 (d can be a data dict or dataCache)
899 """
900 import uuid
901 if file_name:
902 taintfn = d.stamp[file_name] + '.' + task + '.taint'
903 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500904 taintfn = d.getVar('STAMP') + '.' + task + '.taint'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500905 bb.utils.mkdirhier(os.path.dirname(taintfn))
906 # The specific content of the taint file is not really important,
907 # we just need it to be random, so a random UUID is used
908 with open(taintfn, 'w') as taintf:
909 taintf.write(str(uuid.uuid4()))
910
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600911def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500912 """
913 Return the stamp for a given task
914 (d can be a data dict or dataCache)
915 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600916 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500917
918def add_tasks(tasklist, d):
919 task_deps = d.getVar('_task_deps', False)
920 if not task_deps:
921 task_deps = {}
922 if not 'tasks' in task_deps:
923 task_deps['tasks'] = []
924 if not 'parents' in task_deps:
925 task_deps['parents'] = {}
926
927 for task in tasklist:
928 task = d.expand(task)
929
930 d.setVarFlag(task, 'task', 1)
931
932 if not task in task_deps['tasks']:
933 task_deps['tasks'].append(task)
934
935 flags = d.getVarFlags(task)
936 def getTask(name):
937 if not name in task_deps:
938 task_deps[name] = {}
939 if name in flags:
940 deptask = d.expand(flags[name])
Andrew Geissler09036742021-06-25 14:25:14 -0500941 if name in ['noexec', 'fakeroot', 'nostamp']:
942 if deptask != '1':
943 bb.warn("In a future version of BitBake, setting the '{}' flag to something other than '1' "
944 "will result in the flag not being set. See YP bug #13808.".format(name))
945
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500946 task_deps[name][task] = deptask
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800947 getTask('mcdepends')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500948 getTask('depends')
949 getTask('rdepends')
950 getTask('deptask')
951 getTask('rdeptask')
952 getTask('recrdeptask')
953 getTask('recideptask')
954 getTask('nostamp')
955 getTask('fakeroot')
956 getTask('noexec')
957 getTask('umask')
958 task_deps['parents'][task] = []
959 if 'deps' in flags:
960 for dep in flags['deps']:
Brad Bishopc342db32019-05-15 21:57:59 -0400961 # Check and warn for "addtask task after foo" while foo does not exist
962 #if not dep in tasklist:
963 # bb.warn('%s: dependent task %s for %s does not exist' % (d.getVar('PN'), dep, task))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500964 dep = d.expand(dep)
965 task_deps['parents'][task].append(dep)
966
967 # don't assume holding a reference
968 d.setVar('_task_deps', task_deps)
969
970def addtask(task, before, after, d):
971 if task[:3] != "do_":
972 task = "do_" + task
973
974 d.setVarFlag(task, "task", 1)
975 bbtasks = d.getVar('__BBTASKS', False) or []
976 if task not in bbtasks:
977 bbtasks.append(task)
978 d.setVar('__BBTASKS', bbtasks)
979
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500980 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500981 if after is not None:
982 # set up deps for function
983 for entry in after.split():
984 if entry not in existing:
985 existing.append(entry)
986 d.setVarFlag(task, "deps", existing)
987 if before is not None:
988 # set up things that depend on this func
989 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500990 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500991 if task not in existing:
992 d.setVarFlag(entry, "deps", [task] + existing)
993
994def deltask(task, d):
995 if task[:3] != "do_":
996 task = "do_" + task
997
998 bbtasks = d.getVar('__BBTASKS', False) or []
999 if task in bbtasks:
1000 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001001 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001002 d.setVar('__BBTASKS', bbtasks)
1003
1004 d.delVarFlag(task, 'deps')
1005 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001006 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001007 if task in deps:
1008 deps.remove(task)
1009 d.setVarFlag(bbtask, 'deps', deps)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001010
1011def preceedtask(task, with_recrdeptasks, d):
1012 """
1013 Returns a set of tasks in the current recipe which were specified as
1014 precondition by the task itself ("after") or which listed themselves
1015 as precondition ("before"). Preceeding tasks specified via the
1016 "recrdeptask" are included in the result only if requested. Beware
1017 that this may lead to the task itself being listed.
1018 """
1019 preceed = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -04001020
1021 # Ignore tasks which don't exist
1022 tasks = d.getVar('__BBTASKS', False)
1023 if task not in tasks:
1024 return preceed
1025
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001026 preceed.update(d.getVarFlag(task, 'deps') or [])
1027 if with_recrdeptasks:
1028 recrdeptask = d.getVarFlag(task, 'recrdeptask')
1029 if recrdeptask:
1030 preceed.update(recrdeptask.split())
1031 return preceed
1032
1033def tasksbetween(task_start, task_end, d):
1034 """
1035 Return the list of tasks between two tasks in the current recipe,
1036 where task_start is to start at and task_end is the task to end at
1037 (and task_end has a dependency chain back to task_start).
1038 """
1039 outtasks = []
1040 tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
1041 def follow_chain(task, endtask, chain=None):
1042 if not chain:
1043 chain = []
Andrew Geissler5199d832021-09-24 16:47:35 -05001044 if task in chain:
1045 bb.fatal("Circular task dependencies as %s depends on itself via the chain %s" % (task, " -> ".join(chain)))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001046 chain.append(task)
1047 for othertask in tasks:
1048 if othertask == task:
1049 continue
1050 if task == endtask:
1051 for ctask in chain:
1052 if ctask not in outtasks:
1053 outtasks.append(ctask)
1054 else:
1055 deps = d.getVarFlag(othertask, 'deps', False)
1056 if task in deps:
1057 follow_chain(othertask, endtask, chain)
1058 chain.pop()
1059 follow_chain(task_start, task_end)
1060 return outtasks