blob: 30a2ba236f8d76d14f9e850f620f6dd8445345fd [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
18import shlex
19import glob
20import time
21import stat
22import bb
23import bb.msg
24import bb.process
Patrick Williamsc0f7c042017-02-23 20:41:17 -060025import bb.progress
26from bb import data, event, utils
Patrick Williamsc124f4f2015-09-15 14:41:29 -050027
28bblogger = logging.getLogger('BitBake')
29logger = logging.getLogger('BitBake.Build')
30
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031__mtime_cache = {}
32
33def cached_mtime_noerror(f):
34 if f not in __mtime_cache:
35 try:
36 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
37 except OSError:
38 return 0
39 return __mtime_cache[f]
40
41def reset_cache():
42 global __mtime_cache
43 __mtime_cache = {}
44
45# When we execute a Python function, we'd like certain things
46# in all namespaces, hence we add them to __builtins__.
47# If we do not do this and use the exec globals, they will
48# not be available to subfunctions.
Patrick Williamsc0f7c042017-02-23 20:41:17 -060049if hasattr(__builtins__, '__setitem__'):
50 builtins = __builtins__
51else:
52 builtins = __builtins__.__dict__
53
54builtins['bb'] = bb
55builtins['os'] = os
Patrick Williamsc124f4f2015-09-15 14:41:29 -050056
Patrick Williamsc124f4f2015-09-15 14:41:29 -050057class TaskBase(event.Event):
58 """Base class for task events"""
59
60 def __init__(self, t, logfile, d):
61 self._task = t
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()
68 event.Event.__init__(self)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050069 self._message = "recipe %s: task %s: %s" % (d.getVar("PF"), t, self.getDisplayName())
Patrick Williamsc124f4f2015-09-15 14:41:29 -050070
71 def getTask(self):
72 return self._task
73
74 def setTask(self, task):
75 self._task = task
76
77 def getDisplayName(self):
78 return bb.event.getName(self)[4:]
79
80 task = property(getTask, setTask, None, "task property")
81
82class TaskStarted(TaskBase):
83 """Task execution started"""
84 def __init__(self, t, logfile, taskflags, d):
85 super(TaskStarted, self).__init__(t, logfile, d)
86 self.taskflags = taskflags
87
88class TaskSucceeded(TaskBase):
89 """Task execution completed"""
90
91class TaskFailed(TaskBase):
92 """Task execution failed"""
93
94 def __init__(self, task, logfile, metadata, errprinted = False):
95 self.errprinted = errprinted
96 super(TaskFailed, self).__init__(task, logfile, metadata)
97
98class TaskFailedSilent(TaskBase):
99 """Task execution failed (silently)"""
100 def getDisplayName(self):
101 # Don't need to tell the user it was silent
102 return "Failed"
103
104class TaskInvalid(TaskBase):
105
106 def __init__(self, task, metadata):
107 super(TaskInvalid, self).__init__(task, None, metadata)
108 self._message = "No such task '%s'" % task
109
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600110class TaskProgress(event.Event):
111 """
112 Task made some progress that could be reported to the user, usually in
113 the form of a progress bar or similar.
114 NOTE: this class does not inherit from TaskBase since it doesn't need
115 to - it's fired within the task context itself, so we don't have any of
116 the context information that you do in the case of the other events.
117 The event PID can be used to determine which task it came from.
118 The progress value is normally 0-100, but can also be negative
119 indicating that progress has been made but we aren't able to determine
120 how much.
121 The rate is optional, this is simply an extra string to display to the
122 user if specified.
123 """
124 def __init__(self, progress, rate=None):
125 self.progress = progress
126 self.rate = rate
127 event.Event.__init__(self)
128
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500129
130class LogTee(object):
131 def __init__(self, logger, outfile):
132 self.outfile = outfile
133 self.logger = logger
134 self.name = self.outfile.name
135
136 def write(self, string):
137 self.logger.plain(string)
138 self.outfile.write(string)
139
140 def __enter__(self):
141 self.outfile.__enter__()
142 return self
143
144 def __exit__(self, *excinfo):
145 self.outfile.__exit__(*excinfo)
146
147 def __repr__(self):
148 return '<LogTee {0}>'.format(self.name)
Brad Bishop15ae2502019-06-18 21:44:24 -0400149
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150 def flush(self):
151 self.outfile.flush()
152
Brad Bishop15ae2502019-06-18 21:44:24 -0400153
154class StdoutNoopContextManager:
155 """
156 This class acts like sys.stdout, but adds noop __enter__ and __exit__ methods.
157 """
158 def __enter__(self):
159 return sys.stdout
160
161 def __exit__(self, *exc_info):
162 pass
163
164 def write(self, string):
165 return sys.stdout.write(string)
166
167 def flush(self):
168 sys.stdout.flush()
169
170 @property
171 def name(self):
172 return sys.stdout.name
173
174
Brad Bishop08902b02019-08-20 09:16:51 -0400175def exec_func(func, d, dirs = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500176 """Execute a BB 'function'"""
177
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600178 try:
179 oldcwd = os.getcwd()
180 except:
181 oldcwd = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500182
183 flags = d.getVarFlags(func)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500184 cleandirs = flags.get('cleandirs') if flags else None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500185 if cleandirs:
186 for cdir in d.expand(cleandirs).split():
187 bb.utils.remove(cdir, True)
188 bb.utils.mkdirhier(cdir)
189
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500190 if flags and dirs is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500191 dirs = flags.get('dirs')
192 if dirs:
193 dirs = d.expand(dirs).split()
194
195 if dirs:
196 for adir in dirs:
197 bb.utils.mkdirhier(adir)
198 adir = dirs[-1]
199 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600200 adir = None
201
202 body = d.getVar(func, False)
203 if not body:
204 if body is None:
205 logger.warning("Function %s doesn't exist", func)
206 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500207
208 ispython = flags.get('python')
209
210 lockflag = flags.get('lockfiles')
211 if lockflag:
212 lockfiles = [f for f in d.expand(lockflag).split()]
213 else:
214 lockfiles = None
215
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500216 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500217
218 # or func allows items to be executed outside of the normal
219 # task set, such as buildhistory
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500220 task = d.getVar('BB_RUNTASK') or func
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221 if task == func:
222 taskfunc = task
223 else:
224 taskfunc = "%s.%s" % (task, func)
225
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500226 runfmt = d.getVar('BB_RUNFMT') or "run.{func}.{pid}"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500227 runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
228 runfile = os.path.join(tempdir, runfn)
229 bb.utils.mkdirhier(os.path.dirname(runfile))
230
231 # Setup the courtesy link to the runfn, only for tasks
232 # we create the link 'just' before the run script is created
233 # if we create it after, and if the run script fails, then the
234 # link won't be created as an exception would be fired.
235 if task == func:
236 runlink = os.path.join(tempdir, 'run.{0}'.format(task))
237 if runlink:
238 bb.utils.remove(runlink)
239
240 try:
241 os.symlink(runfn, runlink)
242 except OSError:
243 pass
244
245 with bb.utils.fileslocked(lockfiles):
246 if ispython:
Brad Bishop08902b02019-08-20 09:16:51 -0400247 exec_func_python(func, d, runfile, cwd=adir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500248 else:
249 exec_func_shell(func, d, runfile, cwd=adir)
250
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600251 try:
252 curcwd = os.getcwd()
253 except:
254 curcwd = None
255
256 if oldcwd and curcwd != oldcwd:
257 try:
258 bb.warn("Task %s changed cwd to %s" % (func, curcwd))
259 os.chdir(oldcwd)
260 except:
261 pass
262
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500263_functionfmt = """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500264{function}(d)
265"""
266logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
Brad Bishop08902b02019-08-20 09:16:51 -0400267def exec_func_python(func, d, runfile, cwd=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500268 """Execute a python BB 'function'"""
269
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500270 code = _functionfmt.format(function=func)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271 bb.utils.mkdirhier(os.path.dirname(runfile))
272 with open(runfile, 'w') as script:
273 bb.data.emit_func_python(func, script, d)
274
275 if cwd:
276 try:
277 olddir = os.getcwd()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600278 except OSError as e:
279 bb.warn("%s: Cannot get cwd: %s" % (func, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500280 olddir = None
281 os.chdir(cwd)
282
283 bb.debug(2, "Executing python function %s" % func)
284
285 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500286 text = "def %s(d):\n%s" % (func, d.getVar(func, False))
287 fn = d.getVarFlag(func, "filename", False)
288 lineno = int(d.getVarFlag(func, "lineno", False))
289 bb.methodpool.insert_method(func, text, fn, lineno - 1)
290
291 comp = utils.better_compile(code, func, "exec_python_func() autogenerated")
Brad Bishop08902b02019-08-20 09:16:51 -0400292 utils.better_exec(comp, {"d": d}, code, "exec_python_func() autogenerated")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500293 finally:
294 bb.debug(2, "Python function %s finished" % func)
295
296 if cwd and olddir:
297 try:
298 os.chdir(olddir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600299 except OSError as e:
300 bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500301
302def shell_trap_code():
303 return '''#!/bin/sh\n
304# Emit a useful diagnostic if something fails:
305bb_exit_handler() {
306 ret=$?
307 case $ret in
308 0) ;;
309 *) case $BASH_VERSION in
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500310 "") echo "WARNING: exit code $ret from a shell command.";;
311 *) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from '$BASH_COMMAND'";;
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500312 esac
313 exit $ret
314 esac
315}
316trap 'bb_exit_handler' 0
317set -e
318'''
319
Brad Bishop15ae2502019-06-18 21:44:24 -0400320def create_progress_handler(func, progress, logfile, d):
321 if progress == 'percent':
322 # Use default regex
323 return bb.progress.BasicProgressHandler(d, outfile=logfile)
324 elif progress.startswith('percent:'):
325 # Use specified regex
326 return bb.progress.BasicProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
327 elif progress.startswith('outof:'):
328 # Use specified regex
329 return bb.progress.OutOfProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
330 elif progress.startswith("custom:"):
331 # Use a custom progress handler that was injected via OE_EXTRA_IMPORTS or __builtins__
332 import functools
333 from types import ModuleType
334
335 parts = progress.split(":", 2)
336 _, cls, otherargs = parts[0], parts[1], (parts[2] or None) if parts[2:] else None
337 if cls:
338 def resolve(x, y):
339 if not x:
340 return None
341 if isinstance(x, ModuleType):
342 return getattr(x, y, None)
343 return x.get(y)
344 cls_obj = functools.reduce(resolve, cls.split("."), bb.utils._context)
345 if not cls_obj:
346 # Fall-back on __builtins__
347 cls_obj = functools.reduce(lambda x, y: x.get(y), cls.split("."), __builtins__)
348 if cls_obj:
349 return cls_obj(d, outfile=logfile, otherargs=otherargs)
350 bb.warn('%s: unknown custom progress handler in task progress varflag value "%s", ignoring' % (func, cls))
351 else:
352 bb.warn('%s: invalid task progress varflag value "%s", ignoring' % (func, progress))
353
354 return logfile
355
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500356def exec_func_shell(func, d, runfile, cwd=None):
357 """Execute a shell function from the metadata
358
359 Note on directory behavior. The 'dirs' varflag should contain a list
360 of the directories you need created prior to execution. The last
361 item in the list is where we will chdir/cd to.
362 """
363
364 # Don't let the emitted shell script override PWD
365 d.delVarFlag('PWD', 'export')
366
367 with open(runfile, 'w') as script:
368 script.write(shell_trap_code())
369
370 bb.data.emit_func(func, script, d)
371
372 if bb.msg.loggerVerboseLogs:
373 script.write("set -x\n")
374 if cwd:
375 script.write("cd '%s'\n" % cwd)
376 script.write("%s\n" % func)
377 script.write('''
378# cleanup
379ret=$?
380trap '' 0
381exit $ret
382''')
383
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600384 os.chmod(runfile, 0o775)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500385
386 cmd = runfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500387 if d.getVarFlag(func, 'fakeroot', False):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500388 fakerootcmd = d.getVar('FAKEROOT')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500389 if fakerootcmd:
390 cmd = [fakerootcmd, runfile]
391
392 if bb.msg.loggerDefaultVerbose:
Brad Bishop15ae2502019-06-18 21:44:24 -0400393 logfile = LogTee(logger, StdoutNoopContextManager())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500394 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400395 logfile = StdoutNoopContextManager()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500396
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500397 progress = d.getVarFlag(func, 'progress')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600398 if progress:
Brad Bishop15ae2502019-06-18 21:44:24 -0400399 logfile = create_progress_handler(func, progress, logfile, d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600400
401 fifobuffer = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500402 def readfifo(data):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600403 nonlocal fifobuffer
404 fifobuffer.extend(data)
405 while fifobuffer:
406 message, token, nextmsg = fifobuffer.partition(b"\00")
407 if token:
408 splitval = message.split(b' ', 1)
409 cmd = splitval[0].decode("utf-8")
410 if len(splitval) > 1:
411 value = splitval[1].decode("utf-8")
412 else:
413 value = ''
414 if cmd == 'bbplain':
415 bb.plain(value)
416 elif cmd == 'bbnote':
417 bb.note(value)
Brad Bishopc342db32019-05-15 21:57:59 -0400418 elif cmd == 'bbverbnote':
419 bb.verbnote(value)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600420 elif cmd == 'bbwarn':
421 bb.warn(value)
422 elif cmd == 'bberror':
423 bb.error(value)
424 elif cmd == 'bbfatal':
425 # The caller will call exit themselves, so bb.error() is
426 # what we want here rather than bb.fatal()
427 bb.error(value)
428 elif cmd == 'bbfatal_log':
429 bb.error(value, forcelog=True)
430 elif cmd == 'bbdebug':
431 splitval = value.split(' ', 1)
432 level = int(splitval[0])
433 value = splitval[1]
434 bb.debug(level, value)
435 else:
436 bb.warn("Unrecognised command '%s' on FIFO" % cmd)
437 fifobuffer = nextmsg
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500438 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600439 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500440
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500441 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500442 fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
443 if os.path.exists(fifopath):
444 os.unlink(fifopath)
445 os.mkfifo(fifopath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600446 with open(fifopath, 'r+b', buffering=0) as fifo:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500447 try:
448 bb.debug(2, "Executing shell function %s" % func)
Brad Bishop08902b02019-08-20 09:16:51 -0400449 with open(os.devnull, 'r+') as stdin, logfile:
450 bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500451 finally:
452 os.unlink(fifopath)
453
454 bb.debug(2, "Shell function %s finished" % func)
455
456def _task_data(fn, task, d):
457 localdata = bb.data.createCopy(d)
458 localdata.setVar('BB_FILENAME', fn)
459 localdata.setVar('BB_CURRENTTASK', task[3:])
460 localdata.setVar('OVERRIDES', 'task-%s:%s' %
461 (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
462 localdata.finalize()
463 bb.data.expandKeys(localdata)
464 return localdata
465
466def _exec_task(fn, task, d, quieterr):
467 """Execute a BB 'task'
468
469 Execution of a task involves a bit more setup than executing a function,
470 running it with its own local metadata, and with some useful variables set.
471 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500472 if not d.getVarFlag(task, 'task', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500473 event.fire(TaskInvalid(task, d), d)
474 logger.error("No such task: %s" % task)
475 return 1
476
477 logger.debug(1, "Executing task %s", task)
478
479 localdata = _task_data(fn, task, d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500480 tempdir = localdata.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500481 if not tempdir:
482 bb.fatal("T variable not set, unable to build")
483
484 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500485 nice = localdata.getVar("BB_TASK_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500486 if nice:
487 curnice = os.nice(0)
488 nice = int(nice) - curnice
489 newnice = os.nice(nice)
490 logger.debug(1, "Renice to %s " % newnice)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500491 ionice = localdata.getVar("BB_TASK_IONICE_LEVEL")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500492 if ionice:
493 try:
494 cls, prio = ionice.split(".", 1)
495 bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
496 except:
497 bb.warn("Invalid ionice level %s" % ionice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500498
499 bb.utils.mkdirhier(tempdir)
500
501 # Determine the logfile to generate
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500502 logfmt = localdata.getVar('BB_LOGFMT') or 'log.{task}.{pid}'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500503 logbase = logfmt.format(task=task, pid=os.getpid())
504
505 # Document the order of the tasks...
506 logorder = os.path.join(tempdir, 'log.task_order')
507 try:
508 with open(logorder, 'a') as logorderfile:
509 logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
510 except OSError:
511 logger.exception("Opening log file '%s'", logorder)
512 pass
513
514 # Setup the courtesy link to the logfn
515 loglink = os.path.join(tempdir, 'log.{0}'.format(task))
516 logfn = os.path.join(tempdir, logbase)
517 if loglink:
518 bb.utils.remove(loglink)
519
520 try:
521 os.symlink(logbase, loglink)
522 except OSError:
523 pass
524
525 prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
526 postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
527
528 class ErrorCheckHandler(logging.Handler):
529 def __init__(self):
530 self.triggered = False
531 logging.Handler.__init__(self, logging.ERROR)
532 def emit(self, record):
533 if getattr(record, 'forcelog', False):
534 self.triggered = False
535 else:
536 self.triggered = True
537
538 # Handle logfiles
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500539 try:
540 bb.utils.mkdirhier(os.path.dirname(logfn))
541 logfile = open(logfn, 'w')
542 except OSError:
543 logger.exception("Opening log file '%s'", logfn)
544 pass
545
546 # Dup the existing fds so we dont lose them
547 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
548 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
549 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
550
551 # Replace those fds with our own
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800552 with open('/dev/null', 'r') as si:
553 os.dup2(si.fileno(), osi[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500554 os.dup2(logfile.fileno(), oso[1])
555 os.dup2(logfile.fileno(), ose[1])
556
557 # Ensure Python logging goes to the logfile
558 handler = logging.StreamHandler(logfile)
559 handler.setFormatter(logformatter)
560 # Always enable full debug output into task logfiles
561 handler.setLevel(logging.DEBUG - 2)
562 bblogger.addHandler(handler)
563
564 errchk = ErrorCheckHandler()
565 bblogger.addHandler(errchk)
566
567 localdata.setVar('BB_LOGFILE', logfn)
568 localdata.setVar('BB_RUNTASK', task)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500569 localdata.setVar('BB_TASK_LOGGER', bblogger)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500570
571 flags = localdata.getVarFlags(task)
572
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600574 try:
575 event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
576 except (bb.BBHandledException, SystemExit):
577 return 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600578
579 try:
580 for func in (prefuncs or '').split():
581 exec_func(func, localdata)
582 exec_func(task, localdata)
583 for func in (postfuncs or '').split():
584 exec_func(func, localdata)
Brad Bishop08902b02019-08-20 09:16:51 -0400585 except bb.BBHandledException:
586 event.fire(TaskFailed(task, logfn, localdata, True), localdata)
587 return 1
588 except Exception as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600589 if quieterr:
590 event.fire(TaskFailedSilent(task, logfn, localdata), localdata)
591 else:
592 errprinted = errchk.triggered
593 logger.error(str(exc))
594 event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata)
595 return 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500596 finally:
597 sys.stdout.flush()
598 sys.stderr.flush()
599
600 bblogger.removeHandler(handler)
601
602 # Restore the backup fds
603 os.dup2(osi[0], osi[1])
604 os.dup2(oso[0], oso[1])
605 os.dup2(ose[0], ose[1])
606
607 # Close the backup fds
608 os.close(osi[0])
609 os.close(oso[0])
610 os.close(ose[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500611
612 logfile.close()
613 if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
614 logger.debug(2, "Zero size logfn %s, removing", logfn)
615 bb.utils.remove(logfn)
616 bb.utils.remove(loglink)
617 event.fire(TaskSucceeded(task, logfn, localdata), localdata)
618
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500619 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500620 make_stamp(task, localdata)
621
622 return 0
623
624def exec_task(fn, task, d, profile = False):
625 try:
626 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500627 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500628 quieterr = True
629
630 if profile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500631 profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500632 try:
633 import cProfile as profile
634 except:
635 import profile
636 prof = profile.Profile()
637 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
638 prof.dump_stats(profname)
639 bb.utils.process_profilelog(profname)
640
641 return ret
642 else:
643 return _exec_task(fn, task, d, quieterr)
644
645 except Exception:
646 from traceback import format_exc
647 if not quieterr:
648 logger.error("Build of %s failed" % (task))
649 logger.error(format_exc())
650 failedevent = TaskFailed(task, None, d, True)
651 event.fire(failedevent, d)
652 return 1
653
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600654def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500655 """
656 Internal stamp helper function
657 Makes sure the stamp directory exists
658 Returns the stamp path+filename
659
660 In the bitbake core, d can be a CacheData and file_name will be set.
661 When called in task context, d will be a data store, file_name will not be set
662 """
663 taskflagname = taskname
664 if taskname.endswith("_setscene") and taskname != "do_setscene":
665 taskflagname = taskname.replace("_setscene", "")
666
667 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500668 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500669 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
670 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500671 stamp = d.getVar('STAMP')
672 file_name = d.getVar('BB_FILENAME')
673 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500674
675 if baseonly:
676 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600677 if noextra:
678 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500679
680 if not stamp:
681 return
682
683 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
684
685 stampdir = os.path.dirname(stamp)
686 if cached_mtime_noerror(stampdir) == 0:
687 bb.utils.mkdirhier(stampdir)
688
689 return stamp
690
691def stamp_cleanmask_internal(taskname, d, file_name):
692 """
693 Internal stamp helper function to generate stamp cleaning mask
694 Returns the stamp path+filename
695
696 In the bitbake core, d can be a CacheData and file_name will be set.
697 When called in task context, d will be a data store, file_name will not be set
698 """
699 taskflagname = taskname
700 if taskname.endswith("_setscene") and taskname != "do_setscene":
701 taskflagname = taskname.replace("_setscene", "")
702
703 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500704 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500705 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
706 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500707 stamp = d.getVar('STAMPCLEAN')
708 file_name = d.getVar('BB_FILENAME')
709 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710
711 if not stamp:
712 return []
713
714 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
715
716 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
717
718def make_stamp(task, d, file_name = None):
719 """
720 Creates/updates a stamp for a given task
721 (d can be a data dict or dataCache)
722 """
723 cleanmask = stamp_cleanmask_internal(task, d, file_name)
724 for mask in cleanmask:
725 for name in glob.glob(mask):
726 # Preserve sigdata files in the stamps directory
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500727 if "sigdata" in name or "sigbasedata" in name:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500728 continue
729 # Preserve taint files in the stamps directory
730 if name.endswith('.taint'):
731 continue
732 os.unlink(name)
733
734 stamp = stamp_internal(task, d, file_name)
735 # Remove the file and recreate to force timestamp
736 # change on broken NFS filesystems
737 if stamp:
738 bb.utils.remove(stamp)
739 open(stamp, "w").close()
740
741 # If we're in task context, write out a signature file for each task
742 # as it completes
743 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
744 stampbase = stamp_internal(task, d, None, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500745 file_name = d.getVar('BB_FILENAME')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
747
748def del_stamp(task, d, file_name = None):
749 """
750 Removes a stamp for a given task
751 (d can be a data dict or dataCache)
752 """
753 stamp = stamp_internal(task, d, file_name)
754 bb.utils.remove(stamp)
755
756def write_taint(task, d, file_name = None):
757 """
758 Creates a "taint" file which will force the specified task and its
759 dependents to be re-run the next time by influencing the value of its
760 taskhash.
761 (d can be a data dict or dataCache)
762 """
763 import uuid
764 if file_name:
765 taintfn = d.stamp[file_name] + '.' + task + '.taint'
766 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500767 taintfn = d.getVar('STAMP') + '.' + task + '.taint'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500768 bb.utils.mkdirhier(os.path.dirname(taintfn))
769 # The specific content of the taint file is not really important,
770 # we just need it to be random, so a random UUID is used
771 with open(taintfn, 'w') as taintf:
772 taintf.write(str(uuid.uuid4()))
773
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600774def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775 """
776 Return the stamp for a given task
777 (d can be a data dict or dataCache)
778 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600779 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500780
781def add_tasks(tasklist, d):
782 task_deps = d.getVar('_task_deps', False)
783 if not task_deps:
784 task_deps = {}
785 if not 'tasks' in task_deps:
786 task_deps['tasks'] = []
787 if not 'parents' in task_deps:
788 task_deps['parents'] = {}
789
790 for task in tasklist:
791 task = d.expand(task)
792
793 d.setVarFlag(task, 'task', 1)
794
795 if not task in task_deps['tasks']:
796 task_deps['tasks'].append(task)
797
798 flags = d.getVarFlags(task)
799 def getTask(name):
800 if not name in task_deps:
801 task_deps[name] = {}
802 if name in flags:
803 deptask = d.expand(flags[name])
804 task_deps[name][task] = deptask
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800805 getTask('mcdepends')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500806 getTask('depends')
807 getTask('rdepends')
808 getTask('deptask')
809 getTask('rdeptask')
810 getTask('recrdeptask')
811 getTask('recideptask')
812 getTask('nostamp')
813 getTask('fakeroot')
814 getTask('noexec')
815 getTask('umask')
816 task_deps['parents'][task] = []
817 if 'deps' in flags:
818 for dep in flags['deps']:
Brad Bishopc342db32019-05-15 21:57:59 -0400819 # Check and warn for "addtask task after foo" while foo does not exist
820 #if not dep in tasklist:
821 # bb.warn('%s: dependent task %s for %s does not exist' % (d.getVar('PN'), dep, task))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500822 dep = d.expand(dep)
823 task_deps['parents'][task].append(dep)
824
825 # don't assume holding a reference
826 d.setVar('_task_deps', task_deps)
827
828def addtask(task, before, after, d):
829 if task[:3] != "do_":
830 task = "do_" + task
831
832 d.setVarFlag(task, "task", 1)
833 bbtasks = d.getVar('__BBTASKS', False) or []
834 if task not in bbtasks:
835 bbtasks.append(task)
836 d.setVar('__BBTASKS', bbtasks)
837
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500838 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500839 if after is not None:
840 # set up deps for function
841 for entry in after.split():
842 if entry not in existing:
843 existing.append(entry)
844 d.setVarFlag(task, "deps", existing)
845 if before is not None:
846 # set up things that depend on this func
847 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500848 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500849 if task not in existing:
850 d.setVarFlag(entry, "deps", [task] + existing)
851
852def deltask(task, d):
853 if task[:3] != "do_":
854 task = "do_" + task
855
856 bbtasks = d.getVar('__BBTASKS', False) or []
857 if task in bbtasks:
858 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600859 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500860 d.setVar('__BBTASKS', bbtasks)
861
862 d.delVarFlag(task, 'deps')
863 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500864 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500865 if task in deps:
866 deps.remove(task)
867 d.setVarFlag(bbtask, 'deps', deps)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500868
869def preceedtask(task, with_recrdeptasks, d):
870 """
871 Returns a set of tasks in the current recipe which were specified as
872 precondition by the task itself ("after") or which listed themselves
873 as precondition ("before"). Preceeding tasks specified via the
874 "recrdeptask" are included in the result only if requested. Beware
875 that this may lead to the task itself being listed.
876 """
877 preceed = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400878
879 # Ignore tasks which don't exist
880 tasks = d.getVar('__BBTASKS', False)
881 if task not in tasks:
882 return preceed
883
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500884 preceed.update(d.getVarFlag(task, 'deps') or [])
885 if with_recrdeptasks:
886 recrdeptask = d.getVarFlag(task, 'recrdeptask')
887 if recrdeptask:
888 preceed.update(recrdeptask.split())
889 return preceed
890
891def tasksbetween(task_start, task_end, d):
892 """
893 Return the list of tasks between two tasks in the current recipe,
894 where task_start is to start at and task_end is the task to end at
895 (and task_end has a dependency chain back to task_start).
896 """
897 outtasks = []
898 tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
899 def follow_chain(task, endtask, chain=None):
900 if not chain:
901 chain = []
902 chain.append(task)
903 for othertask in tasks:
904 if othertask == task:
905 continue
906 if task == endtask:
907 for ctask in chain:
908 if ctask not in outtasks:
909 outtasks.append(ctask)
910 else:
911 deps = d.getVarFlag(othertask, 'deps', False)
912 if task in deps:
913 follow_chain(othertask, endtask, chain)
914 chain.pop()
915 follow_chain(task_start, task_end)
916 return outtasks