blob: 23b6ee455ff96e051c5b64a1cba068418d630112 [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
19import time
20import stat
21import bb
22import bb.msg
23import bb.process
Patrick Williamsc0f7c042017-02-23 20:41:17 -060024import bb.progress
25from bb import data, event, utils
Patrick Williamsc124f4f2015-09-15 14:41:29 -050026
27bblogger = logging.getLogger('BitBake')
28logger = logging.getLogger('BitBake.Build')
29
Patrick Williamsc124f4f2015-09-15 14:41:29 -050030__mtime_cache = {}
31
32def cached_mtime_noerror(f):
33 if f not in __mtime_cache:
34 try:
35 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
36 except OSError:
37 return 0
38 return __mtime_cache[f]
39
40def reset_cache():
41 global __mtime_cache
42 __mtime_cache = {}
43
44# When we execute a Python function, we'd like certain things
45# in all namespaces, hence we add them to __builtins__.
46# If we do not do this and use the exec globals, they will
47# not be available to subfunctions.
Patrick Williamsc0f7c042017-02-23 20:41:17 -060048if hasattr(__builtins__, '__setitem__'):
49 builtins = __builtins__
50else:
51 builtins = __builtins__.__dict__
52
53builtins['bb'] = bb
54builtins['os'] = os
Patrick Williamsc124f4f2015-09-15 14:41:29 -050055
Patrick Williamsc124f4f2015-09-15 14:41:29 -050056class TaskBase(event.Event):
57 """Base class for task events"""
58
Andrew Geissler82c905d2020-04-13 13:39:40 -050059 def __init__(self, t, fn, logfile, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050060 self._task = t
Andrew Geissler82c905d2020-04-13 13:39:40 -050061 self._fn = fn
Brad Bishop6e60e8b2018-02-01 10:27:11 -050062 self._package = d.getVar("PF")
63 self._mc = d.getVar("BB_CURRENT_MC")
64 self.taskfile = d.getVar("FILE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050065 self.taskname = self._task
66 self.logfile = logfile
67 self.time = time.time()
Andrew Geissler82c905d2020-04-13 13:39:40 -050068 self.pn = d.getVar("PN")
69 self.pv = d.getVar("PV")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050070 event.Event.__init__(self)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050071 self._message = "recipe %s: task %s: %s" % (d.getVar("PF"), t, self.getDisplayName())
Patrick Williamsc124f4f2015-09-15 14:41:29 -050072
73 def getTask(self):
74 return self._task
75
76 def setTask(self, task):
77 self._task = task
78
79 def getDisplayName(self):
80 return bb.event.getName(self)[4:]
81
82 task = property(getTask, setTask, None, "task property")
83
84class TaskStarted(TaskBase):
85 """Task execution started"""
Andrew Geissler82c905d2020-04-13 13:39:40 -050086 def __init__(self, t, fn, logfile, taskflags, d):
87 super(TaskStarted, self).__init__(t, fn, logfile, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050088 self.taskflags = taskflags
89
90class TaskSucceeded(TaskBase):
91 """Task execution completed"""
92
93class TaskFailed(TaskBase):
94 """Task execution failed"""
95
Andrew Geissler82c905d2020-04-13 13:39:40 -050096 def __init__(self, task, fn, logfile, metadata, errprinted = False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050097 self.errprinted = errprinted
Andrew Geissler82c905d2020-04-13 13:39:40 -050098 super(TaskFailed, self).__init__(task, fn, logfile, metadata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050099
100class TaskFailedSilent(TaskBase):
101 """Task execution failed (silently)"""
102 def getDisplayName(self):
103 # Don't need to tell the user it was silent
104 return "Failed"
105
106class TaskInvalid(TaskBase):
107
Andrew Geissler82c905d2020-04-13 13:39:40 -0500108 def __init__(self, task, fn, metadata):
109 super(TaskInvalid, self).__init__(task, fn, None, metadata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500110 self._message = "No such task '%s'" % task
111
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600112class TaskProgress(event.Event):
113 """
114 Task made some progress that could be reported to the user, usually in
115 the form of a progress bar or similar.
116 NOTE: this class does not inherit from TaskBase since it doesn't need
117 to - it's fired within the task context itself, so we don't have any of
118 the context information that you do in the case of the other events.
119 The event PID can be used to determine which task it came from.
120 The progress value is normally 0-100, but can also be negative
121 indicating that progress has been made but we aren't able to determine
122 how much.
123 The rate is optional, this is simply an extra string to display to the
124 user if specified.
125 """
126 def __init__(self, progress, rate=None):
127 self.progress = progress
128 self.rate = rate
129 event.Event.__init__(self)
130
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131
132class LogTee(object):
133 def __init__(self, logger, outfile):
134 self.outfile = outfile
135 self.logger = logger
136 self.name = self.outfile.name
137
138 def write(self, string):
139 self.logger.plain(string)
140 self.outfile.write(string)
141
142 def __enter__(self):
143 self.outfile.__enter__()
144 return self
145
146 def __exit__(self, *excinfo):
147 self.outfile.__exit__(*excinfo)
148
149 def __repr__(self):
150 return '<LogTee {0}>'.format(self.name)
Brad Bishop15ae2502019-06-18 21:44:24 -0400151
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500152 def flush(self):
153 self.outfile.flush()
154
Brad Bishop15ae2502019-06-18 21:44:24 -0400155
156class StdoutNoopContextManager:
157 """
158 This class acts like sys.stdout, but adds noop __enter__ and __exit__ methods.
159 """
160 def __enter__(self):
161 return sys.stdout
162
163 def __exit__(self, *exc_info):
164 pass
165
166 def write(self, string):
167 return sys.stdout.write(string)
168
169 def flush(self):
170 sys.stdout.flush()
171
172 @property
173 def name(self):
174 return sys.stdout.name
175
176
Brad Bishop08902b02019-08-20 09:16:51 -0400177def exec_func(func, d, dirs = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500178 """Execute a BB 'function'"""
179
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600180 try:
181 oldcwd = os.getcwd()
182 except:
183 oldcwd = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500184
185 flags = d.getVarFlags(func)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500186 cleandirs = flags.get('cleandirs') if flags else None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500187 if cleandirs:
188 for cdir in d.expand(cleandirs).split():
189 bb.utils.remove(cdir, True)
190 bb.utils.mkdirhier(cdir)
191
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500192 if flags and dirs is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500193 dirs = flags.get('dirs')
194 if dirs:
195 dirs = d.expand(dirs).split()
196
197 if dirs:
198 for adir in dirs:
199 bb.utils.mkdirhier(adir)
200 adir = dirs[-1]
201 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600202 adir = None
203
204 body = d.getVar(func, False)
205 if not body:
206 if body is None:
207 logger.warning("Function %s doesn't exist", func)
208 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500209
210 ispython = flags.get('python')
211
212 lockflag = flags.get('lockfiles')
213 if lockflag:
214 lockfiles = [f for f in d.expand(lockflag).split()]
215 else:
216 lockfiles = None
217
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500218 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500219
220 # or func allows items to be executed outside of the normal
221 # task set, such as buildhistory
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500222 task = d.getVar('BB_RUNTASK') or func
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500223 if task == func:
224 taskfunc = task
225 else:
226 taskfunc = "%s.%s" % (task, func)
227
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500228 runfmt = d.getVar('BB_RUNFMT') or "run.{func}.{pid}"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500229 runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
230 runfile = os.path.join(tempdir, runfn)
231 bb.utils.mkdirhier(os.path.dirname(runfile))
232
233 # Setup the courtesy link to the runfn, only for tasks
234 # we create the link 'just' before the run script is created
235 # if we create it after, and if the run script fails, then the
236 # link won't be created as an exception would be fired.
237 if task == func:
238 runlink = os.path.join(tempdir, 'run.{0}'.format(task))
239 if runlink:
240 bb.utils.remove(runlink)
241
242 try:
243 os.symlink(runfn, runlink)
244 except OSError:
245 pass
246
247 with bb.utils.fileslocked(lockfiles):
248 if ispython:
Brad Bishop08902b02019-08-20 09:16:51 -0400249 exec_func_python(func, d, runfile, cwd=adir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500250 else:
251 exec_func_shell(func, d, runfile, cwd=adir)
252
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600253 try:
254 curcwd = os.getcwd()
255 except:
256 curcwd = None
257
258 if oldcwd and curcwd != oldcwd:
259 try:
260 bb.warn("Task %s changed cwd to %s" % (func, curcwd))
261 os.chdir(oldcwd)
262 except:
263 pass
264
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500265_functionfmt = """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500266{function}(d)
267"""
268logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
Brad Bishop08902b02019-08-20 09:16:51 -0400269def exec_func_python(func, d, runfile, cwd=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500270 """Execute a python BB 'function'"""
271
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500272 code = _functionfmt.format(function=func)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500273 bb.utils.mkdirhier(os.path.dirname(runfile))
274 with open(runfile, 'w') as script:
275 bb.data.emit_func_python(func, script, d)
276
277 if cwd:
278 try:
279 olddir = os.getcwd()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600280 except OSError as e:
281 bb.warn("%s: Cannot get cwd: %s" % (func, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500282 olddir = None
283 os.chdir(cwd)
284
285 bb.debug(2, "Executing python function %s" % func)
286
287 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500288 text = "def %s(d):\n%s" % (func, d.getVar(func, False))
289 fn = d.getVarFlag(func, "filename", False)
290 lineno = int(d.getVarFlag(func, "lineno", False))
291 bb.methodpool.insert_method(func, text, fn, lineno - 1)
292
293 comp = utils.better_compile(code, func, "exec_python_func() autogenerated")
Brad Bishop08902b02019-08-20 09:16:51 -0400294 utils.better_exec(comp, {"d": d}, code, "exec_python_func() autogenerated")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500295 finally:
296 bb.debug(2, "Python function %s finished" % func)
297
298 if cwd and olddir:
299 try:
300 os.chdir(olddir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600301 except OSError as e:
302 bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500303
304def shell_trap_code():
305 return '''#!/bin/sh\n
306# Emit a useful diagnostic if something fails:
307bb_exit_handler() {
308 ret=$?
309 case $ret in
310 0) ;;
311 *) case $BASH_VERSION in
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500312 "") echo "WARNING: exit code $ret from a shell command.";;
313 *) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from '$BASH_COMMAND'";;
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500314 esac
315 exit $ret
316 esac
317}
318trap 'bb_exit_handler' 0
319set -e
320'''
321
Brad Bishop15ae2502019-06-18 21:44:24 -0400322def create_progress_handler(func, progress, logfile, d):
323 if progress == 'percent':
324 # Use default regex
325 return bb.progress.BasicProgressHandler(d, outfile=logfile)
326 elif progress.startswith('percent:'):
327 # Use specified regex
328 return bb.progress.BasicProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
329 elif progress.startswith('outof:'):
330 # Use specified regex
331 return bb.progress.OutOfProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
332 elif progress.startswith("custom:"):
333 # Use a custom progress handler that was injected via OE_EXTRA_IMPORTS or __builtins__
334 import functools
335 from types import ModuleType
336
337 parts = progress.split(":", 2)
338 _, cls, otherargs = parts[0], parts[1], (parts[2] or None) if parts[2:] else None
339 if cls:
340 def resolve(x, y):
341 if not x:
342 return None
343 if isinstance(x, ModuleType):
344 return getattr(x, y, None)
345 return x.get(y)
346 cls_obj = functools.reduce(resolve, cls.split("."), bb.utils._context)
347 if not cls_obj:
348 # Fall-back on __builtins__
349 cls_obj = functools.reduce(lambda x, y: x.get(y), cls.split("."), __builtins__)
350 if cls_obj:
351 return cls_obj(d, outfile=logfile, otherargs=otherargs)
352 bb.warn('%s: unknown custom progress handler in task progress varflag value "%s", ignoring' % (func, cls))
353 else:
354 bb.warn('%s: invalid task progress varflag value "%s", ignoring' % (func, progress))
355
356 return logfile
357
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500358def exec_func_shell(func, d, runfile, cwd=None):
359 """Execute a shell function from the metadata
360
361 Note on directory behavior. The 'dirs' varflag should contain a list
362 of the directories you need created prior to execution. The last
363 item in the list is where we will chdir/cd to.
364 """
365
366 # Don't let the emitted shell script override PWD
367 d.delVarFlag('PWD', 'export')
368
369 with open(runfile, 'w') as script:
370 script.write(shell_trap_code())
371
372 bb.data.emit_func(func, script, d)
373
374 if bb.msg.loggerVerboseLogs:
375 script.write("set -x\n")
376 if cwd:
377 script.write("cd '%s'\n" % cwd)
378 script.write("%s\n" % func)
379 script.write('''
380# cleanup
381ret=$?
382trap '' 0
383exit $ret
384''')
385
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600386 os.chmod(runfile, 0o775)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500387
388 cmd = runfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500389 if d.getVarFlag(func, 'fakeroot', False):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500390 fakerootcmd = d.getVar('FAKEROOT')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500391 if fakerootcmd:
392 cmd = [fakerootcmd, runfile]
393
394 if bb.msg.loggerDefaultVerbose:
Brad Bishop15ae2502019-06-18 21:44:24 -0400395 logfile = LogTee(logger, StdoutNoopContextManager())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500396 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400397 logfile = StdoutNoopContextManager()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500398
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500399 progress = d.getVarFlag(func, 'progress')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600400 if progress:
Brad Bishop15ae2502019-06-18 21:44:24 -0400401 logfile = create_progress_handler(func, progress, logfile, d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600402
403 fifobuffer = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500404 def readfifo(data):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600405 nonlocal fifobuffer
406 fifobuffer.extend(data)
407 while fifobuffer:
408 message, token, nextmsg = fifobuffer.partition(b"\00")
409 if token:
410 splitval = message.split(b' ', 1)
411 cmd = splitval[0].decode("utf-8")
412 if len(splitval) > 1:
413 value = splitval[1].decode("utf-8")
414 else:
415 value = ''
416 if cmd == 'bbplain':
417 bb.plain(value)
418 elif cmd == 'bbnote':
419 bb.note(value)
Brad Bishopc342db32019-05-15 21:57:59 -0400420 elif cmd == 'bbverbnote':
421 bb.verbnote(value)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600422 elif cmd == 'bbwarn':
423 bb.warn(value)
424 elif cmd == 'bberror':
425 bb.error(value)
426 elif cmd == 'bbfatal':
427 # The caller will call exit themselves, so bb.error() is
428 # what we want here rather than bb.fatal()
429 bb.error(value)
430 elif cmd == 'bbfatal_log':
431 bb.error(value, forcelog=True)
432 elif cmd == 'bbdebug':
433 splitval = value.split(' ', 1)
434 level = int(splitval[0])
435 value = splitval[1]
436 bb.debug(level, value)
437 else:
438 bb.warn("Unrecognised command '%s' on FIFO" % cmd)
439 fifobuffer = nextmsg
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500440 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600441 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500442
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500443 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444 fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
445 if os.path.exists(fifopath):
446 os.unlink(fifopath)
447 os.mkfifo(fifopath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600448 with open(fifopath, 'r+b', buffering=0) as fifo:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500449 try:
450 bb.debug(2, "Executing shell function %s" % func)
Brad Bishop08902b02019-08-20 09:16:51 -0400451 with open(os.devnull, 'r+') as stdin, logfile:
452 bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500453 finally:
454 os.unlink(fifopath)
455
456 bb.debug(2, "Shell function %s finished" % func)
457
458def _task_data(fn, task, d):
459 localdata = bb.data.createCopy(d)
460 localdata.setVar('BB_FILENAME', fn)
461 localdata.setVar('BB_CURRENTTASK', task[3:])
462 localdata.setVar('OVERRIDES', 'task-%s:%s' %
463 (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
464 localdata.finalize()
465 bb.data.expandKeys(localdata)
466 return localdata
467
468def _exec_task(fn, task, d, quieterr):
469 """Execute a BB 'task'
470
471 Execution of a task involves a bit more setup than executing a function,
472 running it with its own local metadata, and with some useful variables set.
473 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500474 if not d.getVarFlag(task, 'task', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500475 event.fire(TaskInvalid(task, d), d)
476 logger.error("No such task: %s" % task)
477 return 1
478
479 logger.debug(1, "Executing task %s", task)
480
481 localdata = _task_data(fn, task, d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500482 tempdir = localdata.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500483 if not tempdir:
484 bb.fatal("T variable not set, unable to build")
485
486 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500487 nice = localdata.getVar("BB_TASK_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488 if nice:
489 curnice = os.nice(0)
490 nice = int(nice) - curnice
491 newnice = os.nice(nice)
492 logger.debug(1, "Renice to %s " % newnice)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500493 ionice = localdata.getVar("BB_TASK_IONICE_LEVEL")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500494 if ionice:
495 try:
496 cls, prio = ionice.split(".", 1)
497 bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
498 except:
499 bb.warn("Invalid ionice level %s" % ionice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500500
501 bb.utils.mkdirhier(tempdir)
502
503 # Determine the logfile to generate
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500504 logfmt = localdata.getVar('BB_LOGFMT') or 'log.{task}.{pid}'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500505 logbase = logfmt.format(task=task, pid=os.getpid())
506
507 # Document the order of the tasks...
508 logorder = os.path.join(tempdir, 'log.task_order')
509 try:
510 with open(logorder, 'a') as logorderfile:
511 logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
512 except OSError:
513 logger.exception("Opening log file '%s'", logorder)
514 pass
515
516 # Setup the courtesy link to the logfn
517 loglink = os.path.join(tempdir, 'log.{0}'.format(task))
518 logfn = os.path.join(tempdir, logbase)
519 if loglink:
520 bb.utils.remove(loglink)
521
522 try:
523 os.symlink(logbase, loglink)
524 except OSError:
525 pass
526
527 prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
528 postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
529
530 class ErrorCheckHandler(logging.Handler):
531 def __init__(self):
532 self.triggered = False
533 logging.Handler.__init__(self, logging.ERROR)
534 def emit(self, record):
535 if getattr(record, 'forcelog', False):
536 self.triggered = False
537 else:
538 self.triggered = True
539
540 # Handle logfiles
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500541 try:
542 bb.utils.mkdirhier(os.path.dirname(logfn))
543 logfile = open(logfn, 'w')
544 except OSError:
545 logger.exception("Opening log file '%s'", logfn)
546 pass
547
548 # Dup the existing fds so we dont lose them
549 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
550 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
551 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
552
553 # Replace those fds with our own
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800554 with open('/dev/null', 'r') as si:
555 os.dup2(si.fileno(), osi[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500556 os.dup2(logfile.fileno(), oso[1])
557 os.dup2(logfile.fileno(), ose[1])
558
559 # Ensure Python logging goes to the logfile
560 handler = logging.StreamHandler(logfile)
561 handler.setFormatter(logformatter)
562 # Always enable full debug output into task logfiles
563 handler.setLevel(logging.DEBUG - 2)
564 bblogger.addHandler(handler)
565
566 errchk = ErrorCheckHandler()
567 bblogger.addHandler(errchk)
568
569 localdata.setVar('BB_LOGFILE', logfn)
570 localdata.setVar('BB_RUNTASK', task)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500571 localdata.setVar('BB_TASK_LOGGER', bblogger)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500572
573 flags = localdata.getVarFlags(task)
574
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500575 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600576 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500577 event.fire(TaskStarted(task, fn, logfn, flags, localdata), localdata)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600578 except (bb.BBHandledException, SystemExit):
579 return 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600580
581 try:
582 for func in (prefuncs or '').split():
583 exec_func(func, localdata)
584 exec_func(task, localdata)
585 for func in (postfuncs or '').split():
586 exec_func(func, localdata)
Brad Bishop08902b02019-08-20 09:16:51 -0400587 except bb.BBHandledException:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500588 event.fire(TaskFailed(task, fn, logfn, localdata, True), localdata)
Brad Bishop08902b02019-08-20 09:16:51 -0400589 return 1
590 except Exception as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600591 if quieterr:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500592 event.fire(TaskFailedSilent(task, fn, logfn, localdata), localdata)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600593 else:
594 errprinted = errchk.triggered
595 logger.error(str(exc))
Andrew Geissler82c905d2020-04-13 13:39:40 -0500596 event.fire(TaskFailed(task, fn, logfn, localdata, errprinted), localdata)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600597 return 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500598 finally:
599 sys.stdout.flush()
600 sys.stderr.flush()
601
602 bblogger.removeHandler(handler)
603
604 # Restore the backup fds
605 os.dup2(osi[0], osi[1])
606 os.dup2(oso[0], oso[1])
607 os.dup2(ose[0], ose[1])
608
609 # Close the backup fds
610 os.close(osi[0])
611 os.close(oso[0])
612 os.close(ose[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500613
614 logfile.close()
615 if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
616 logger.debug(2, "Zero size logfn %s, removing", logfn)
617 bb.utils.remove(logfn)
618 bb.utils.remove(loglink)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500619 event.fire(TaskSucceeded(task, fn, logfn, localdata), localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500620
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500621 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500622 make_stamp(task, localdata)
623
624 return 0
625
626def exec_task(fn, task, d, profile = False):
627 try:
628 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500629 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500630 quieterr = True
631
632 if profile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500633 profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500634 try:
635 import cProfile as profile
636 except:
637 import profile
638 prof = profile.Profile()
639 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
640 prof.dump_stats(profname)
641 bb.utils.process_profilelog(profname)
642
643 return ret
644 else:
645 return _exec_task(fn, task, d, quieterr)
646
647 except Exception:
648 from traceback import format_exc
649 if not quieterr:
650 logger.error("Build of %s failed" % (task))
651 logger.error(format_exc())
652 failedevent = TaskFailed(task, None, d, True)
653 event.fire(failedevent, d)
654 return 1
655
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600656def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500657 """
658 Internal stamp helper function
659 Makes sure the stamp directory exists
660 Returns the stamp path+filename
661
662 In the bitbake core, d can be a CacheData and file_name will be set.
663 When called in task context, d will be a data store, file_name will not be set
664 """
665 taskflagname = taskname
666 if taskname.endswith("_setscene") and taskname != "do_setscene":
667 taskflagname = taskname.replace("_setscene", "")
668
669 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500670 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500671 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
672 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500673 stamp = d.getVar('STAMP')
674 file_name = d.getVar('BB_FILENAME')
675 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500676
677 if baseonly:
678 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600679 if noextra:
680 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500681
682 if not stamp:
683 return
684
685 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
686
687 stampdir = os.path.dirname(stamp)
688 if cached_mtime_noerror(stampdir) == 0:
689 bb.utils.mkdirhier(stampdir)
690
691 return stamp
692
693def stamp_cleanmask_internal(taskname, d, file_name):
694 """
695 Internal stamp helper function to generate stamp cleaning mask
696 Returns the stamp path+filename
697
698 In the bitbake core, d can be a CacheData and file_name will be set.
699 When called in task context, d will be a data store, file_name will not be set
700 """
701 taskflagname = taskname
702 if taskname.endswith("_setscene") and taskname != "do_setscene":
703 taskflagname = taskname.replace("_setscene", "")
704
705 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500706 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
708 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500709 stamp = d.getVar('STAMPCLEAN')
710 file_name = d.getVar('BB_FILENAME')
711 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500712
713 if not stamp:
714 return []
715
716 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
717
718 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
719
720def make_stamp(task, d, file_name = None):
721 """
722 Creates/updates a stamp for a given task
723 (d can be a data dict or dataCache)
724 """
725 cleanmask = stamp_cleanmask_internal(task, d, file_name)
726 for mask in cleanmask:
727 for name in glob.glob(mask):
728 # Preserve sigdata files in the stamps directory
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500729 if "sigdata" in name or "sigbasedata" in name:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500730 continue
731 # Preserve taint files in the stamps directory
732 if name.endswith('.taint'):
733 continue
734 os.unlink(name)
735
736 stamp = stamp_internal(task, d, file_name)
737 # Remove the file and recreate to force timestamp
738 # change on broken NFS filesystems
739 if stamp:
740 bb.utils.remove(stamp)
741 open(stamp, "w").close()
742
743 # If we're in task context, write out a signature file for each task
744 # as it completes
745 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
746 stampbase = stamp_internal(task, d, None, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500747 file_name = d.getVar('BB_FILENAME')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500748 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
749
750def del_stamp(task, d, file_name = None):
751 """
752 Removes a stamp for a given task
753 (d can be a data dict or dataCache)
754 """
755 stamp = stamp_internal(task, d, file_name)
756 bb.utils.remove(stamp)
757
758def write_taint(task, d, file_name = None):
759 """
760 Creates a "taint" file which will force the specified task and its
761 dependents to be re-run the next time by influencing the value of its
762 taskhash.
763 (d can be a data dict or dataCache)
764 """
765 import uuid
766 if file_name:
767 taintfn = d.stamp[file_name] + '.' + task + '.taint'
768 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500769 taintfn = d.getVar('STAMP') + '.' + task + '.taint'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500770 bb.utils.mkdirhier(os.path.dirname(taintfn))
771 # The specific content of the taint file is not really important,
772 # we just need it to be random, so a random UUID is used
773 with open(taintfn, 'w') as taintf:
774 taintf.write(str(uuid.uuid4()))
775
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600776def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500777 """
778 Return the stamp for a given task
779 (d can be a data dict or dataCache)
780 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600781 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500782
783def add_tasks(tasklist, d):
784 task_deps = d.getVar('_task_deps', False)
785 if not task_deps:
786 task_deps = {}
787 if not 'tasks' in task_deps:
788 task_deps['tasks'] = []
789 if not 'parents' in task_deps:
790 task_deps['parents'] = {}
791
792 for task in tasklist:
793 task = d.expand(task)
794
795 d.setVarFlag(task, 'task', 1)
796
797 if not task in task_deps['tasks']:
798 task_deps['tasks'].append(task)
799
800 flags = d.getVarFlags(task)
801 def getTask(name):
802 if not name in task_deps:
803 task_deps[name] = {}
804 if name in flags:
805 deptask = d.expand(flags[name])
806 task_deps[name][task] = deptask
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800807 getTask('mcdepends')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500808 getTask('depends')
809 getTask('rdepends')
810 getTask('deptask')
811 getTask('rdeptask')
812 getTask('recrdeptask')
813 getTask('recideptask')
814 getTask('nostamp')
815 getTask('fakeroot')
816 getTask('noexec')
817 getTask('umask')
818 task_deps['parents'][task] = []
819 if 'deps' in flags:
820 for dep in flags['deps']:
Brad Bishopc342db32019-05-15 21:57:59 -0400821 # Check and warn for "addtask task after foo" while foo does not exist
822 #if not dep in tasklist:
823 # bb.warn('%s: dependent task %s for %s does not exist' % (d.getVar('PN'), dep, task))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500824 dep = d.expand(dep)
825 task_deps['parents'][task].append(dep)
826
827 # don't assume holding a reference
828 d.setVar('_task_deps', task_deps)
829
830def addtask(task, before, after, d):
831 if task[:3] != "do_":
832 task = "do_" + task
833
834 d.setVarFlag(task, "task", 1)
835 bbtasks = d.getVar('__BBTASKS', False) or []
836 if task not in bbtasks:
837 bbtasks.append(task)
838 d.setVar('__BBTASKS', bbtasks)
839
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500840 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500841 if after is not None:
842 # set up deps for function
843 for entry in after.split():
844 if entry not in existing:
845 existing.append(entry)
846 d.setVarFlag(task, "deps", existing)
847 if before is not None:
848 # set up things that depend on this func
849 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500850 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851 if task not in existing:
852 d.setVarFlag(entry, "deps", [task] + existing)
853
854def deltask(task, d):
855 if task[:3] != "do_":
856 task = "do_" + task
857
858 bbtasks = d.getVar('__BBTASKS', False) or []
859 if task in bbtasks:
860 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600861 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500862 d.setVar('__BBTASKS', bbtasks)
863
864 d.delVarFlag(task, 'deps')
865 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500866 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500867 if task in deps:
868 deps.remove(task)
869 d.setVarFlag(bbtask, 'deps', deps)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500870
871def preceedtask(task, with_recrdeptasks, d):
872 """
873 Returns a set of tasks in the current recipe which were specified as
874 precondition by the task itself ("after") or which listed themselves
875 as precondition ("before"). Preceeding tasks specified via the
876 "recrdeptask" are included in the result only if requested. Beware
877 that this may lead to the task itself being listed.
878 """
879 preceed = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400880
881 # Ignore tasks which don't exist
882 tasks = d.getVar('__BBTASKS', False)
883 if task not in tasks:
884 return preceed
885
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500886 preceed.update(d.getVarFlag(task, 'deps') or [])
887 if with_recrdeptasks:
888 recrdeptask = d.getVarFlag(task, 'recrdeptask')
889 if recrdeptask:
890 preceed.update(recrdeptask.split())
891 return preceed
892
893def tasksbetween(task_start, task_end, d):
894 """
895 Return the list of tasks between two tasks in the current recipe,
896 where task_start is to start at and task_end is the task to end at
897 (and task_end has a dependency chain back to task_start).
898 """
899 outtasks = []
900 tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
901 def follow_chain(task, endtask, chain=None):
902 if not chain:
903 chain = []
904 chain.append(task)
905 for othertask in tasks:
906 if othertask == task:
907 continue
908 if task == endtask:
909 for ctask in chain:
910 if ctask not in outtasks:
911 outtasks.append(ctask)
912 else:
913 deps = d.getVarFlag(othertask, 'deps', False)
914 if task in deps:
915 follow_chain(othertask, endtask, chain)
916 chain.pop()
917 follow_chain(task_start, task_end)
918 return outtasks