blob: 7571421d71b2be954a3c58e2211e1a1b21a6b582 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# BitBake 'Build' implementation
5#
6# Core code for function execution and task handling in the
7# BitBake build tools.
8#
9# Copyright (C) 2003, 2004 Chris Larson
10#
11# Based on Gentoo's portage.py.
12#
13# This program is free software; you can redistribute it and/or modify
14# it under the terms of the GNU General Public License version 2 as
15# published by the Free Software Foundation.
16#
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20# GNU General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License along
23# with this program; if not, write to the Free Software Foundation, Inc.,
24# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25#
26# Based on functions from the base bb module, Copyright 2003 Holger Schurig
27
28import os
29import sys
30import logging
31import shlex
32import glob
33import time
34import stat
35import bb
36import bb.msg
37import bb.process
Patrick Williamsc0f7c042017-02-23 20:41:17 -060038import bb.progress
39from bb import data, event, utils
Patrick Williamsc124f4f2015-09-15 14:41:29 -050040
41bblogger = logging.getLogger('BitBake')
42logger = logging.getLogger('BitBake.Build')
43
Patrick Williamsc124f4f2015-09-15 14:41:29 -050044__mtime_cache = {}
45
46def cached_mtime_noerror(f):
47 if f not in __mtime_cache:
48 try:
49 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
50 except OSError:
51 return 0
52 return __mtime_cache[f]
53
54def reset_cache():
55 global __mtime_cache
56 __mtime_cache = {}
57
58# When we execute a Python function, we'd like certain things
59# in all namespaces, hence we add them to __builtins__.
60# If we do not do this and use the exec globals, they will
61# not be available to subfunctions.
Patrick Williamsc0f7c042017-02-23 20:41:17 -060062if hasattr(__builtins__, '__setitem__'):
63 builtins = __builtins__
64else:
65 builtins = __builtins__.__dict__
66
67builtins['bb'] = bb
68builtins['os'] = os
Patrick Williamsc124f4f2015-09-15 14:41:29 -050069
70class FuncFailed(Exception):
71 def __init__(self, name = None, logfile = None):
72 self.logfile = logfile
73 self.name = name
74 if name:
75 self.msg = 'Function failed: %s' % name
76 else:
77 self.msg = "Function failed"
78
79 def __str__(self):
80 if self.logfile and os.path.exists(self.logfile):
81 msg = ("%s (log file is located at %s)" %
82 (self.msg, self.logfile))
83 else:
84 msg = self.msg
85 return msg
86
87class TaskBase(event.Event):
88 """Base class for task events"""
89
90 def __init__(self, t, logfile, d):
91 self._task = t
Brad Bishop6e60e8b2018-02-01 10:27:11 -050092 self._package = d.getVar("PF")
93 self._mc = d.getVar("BB_CURRENT_MC")
94 self.taskfile = d.getVar("FILE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050095 self.taskname = self._task
96 self.logfile = logfile
97 self.time = time.time()
98 event.Event.__init__(self)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050099 self._message = "recipe %s: task %s: %s" % (d.getVar("PF"), t, self.getDisplayName())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500100
101 def getTask(self):
102 return self._task
103
104 def setTask(self, task):
105 self._task = task
106
107 def getDisplayName(self):
108 return bb.event.getName(self)[4:]
109
110 task = property(getTask, setTask, None, "task property")
111
112class TaskStarted(TaskBase):
113 """Task execution started"""
114 def __init__(self, t, logfile, taskflags, d):
115 super(TaskStarted, self).__init__(t, logfile, d)
116 self.taskflags = taskflags
117
118class TaskSucceeded(TaskBase):
119 """Task execution completed"""
120
121class TaskFailed(TaskBase):
122 """Task execution failed"""
123
124 def __init__(self, task, logfile, metadata, errprinted = False):
125 self.errprinted = errprinted
126 super(TaskFailed, self).__init__(task, logfile, metadata)
127
128class TaskFailedSilent(TaskBase):
129 """Task execution failed (silently)"""
130 def getDisplayName(self):
131 # Don't need to tell the user it was silent
132 return "Failed"
133
134class TaskInvalid(TaskBase):
135
136 def __init__(self, task, metadata):
137 super(TaskInvalid, self).__init__(task, None, metadata)
138 self._message = "No such task '%s'" % task
139
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600140class TaskProgress(event.Event):
141 """
142 Task made some progress that could be reported to the user, usually in
143 the form of a progress bar or similar.
144 NOTE: this class does not inherit from TaskBase since it doesn't need
145 to - it's fired within the task context itself, so we don't have any of
146 the context information that you do in the case of the other events.
147 The event PID can be used to determine which task it came from.
148 The progress value is normally 0-100, but can also be negative
149 indicating that progress has been made but we aren't able to determine
150 how much.
151 The rate is optional, this is simply an extra string to display to the
152 user if specified.
153 """
154 def __init__(self, progress, rate=None):
155 self.progress = progress
156 self.rate = rate
157 event.Event.__init__(self)
158
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500159
160class LogTee(object):
161 def __init__(self, logger, outfile):
162 self.outfile = outfile
163 self.logger = logger
164 self.name = self.outfile.name
165
166 def write(self, string):
167 self.logger.plain(string)
168 self.outfile.write(string)
169
170 def __enter__(self):
171 self.outfile.__enter__()
172 return self
173
174 def __exit__(self, *excinfo):
175 self.outfile.__exit__(*excinfo)
176
177 def __repr__(self):
178 return '<LogTee {0}>'.format(self.name)
179 def flush(self):
180 self.outfile.flush()
181
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500182#
183# pythonexception allows the python exceptions generated to be raised
184# as the real exceptions (not FuncFailed) and without a backtrace at the
185# origin of the failure.
186#
187def exec_func(func, d, dirs = None, pythonexception=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500188 """Execute a BB 'function'"""
189
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600190 try:
191 oldcwd = os.getcwd()
192 except:
193 oldcwd = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500194
195 flags = d.getVarFlags(func)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500196 cleandirs = flags.get('cleandirs') if flags else None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500197 if cleandirs:
198 for cdir in d.expand(cleandirs).split():
199 bb.utils.remove(cdir, True)
200 bb.utils.mkdirhier(cdir)
201
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500202 if flags and dirs is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500203 dirs = flags.get('dirs')
204 if dirs:
205 dirs = d.expand(dirs).split()
206
207 if dirs:
208 for adir in dirs:
209 bb.utils.mkdirhier(adir)
210 adir = dirs[-1]
211 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600212 adir = None
213
214 body = d.getVar(func, False)
215 if not body:
216 if body is None:
217 logger.warning("Function %s doesn't exist", func)
218 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500219
220 ispython = flags.get('python')
221
222 lockflag = flags.get('lockfiles')
223 if lockflag:
224 lockfiles = [f for f in d.expand(lockflag).split()]
225 else:
226 lockfiles = None
227
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500228 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500229
230 # or func allows items to be executed outside of the normal
231 # task set, such as buildhistory
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500232 task = d.getVar('BB_RUNTASK') or func
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500233 if task == func:
234 taskfunc = task
235 else:
236 taskfunc = "%s.%s" % (task, func)
237
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500238 runfmt = d.getVar('BB_RUNFMT') or "run.{func}.{pid}"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500239 runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
240 runfile = os.path.join(tempdir, runfn)
241 bb.utils.mkdirhier(os.path.dirname(runfile))
242
243 # Setup the courtesy link to the runfn, only for tasks
244 # we create the link 'just' before the run script is created
245 # if we create it after, and if the run script fails, then the
246 # link won't be created as an exception would be fired.
247 if task == func:
248 runlink = os.path.join(tempdir, 'run.{0}'.format(task))
249 if runlink:
250 bb.utils.remove(runlink)
251
252 try:
253 os.symlink(runfn, runlink)
254 except OSError:
255 pass
256
257 with bb.utils.fileslocked(lockfiles):
258 if ispython:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500259 exec_func_python(func, d, runfile, cwd=adir, pythonexception=pythonexception)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500260 else:
261 exec_func_shell(func, d, runfile, cwd=adir)
262
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600263 try:
264 curcwd = os.getcwd()
265 except:
266 curcwd = None
267
268 if oldcwd and curcwd != oldcwd:
269 try:
270 bb.warn("Task %s changed cwd to %s" % (func, curcwd))
271 os.chdir(oldcwd)
272 except:
273 pass
274
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500275_functionfmt = """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500276{function}(d)
277"""
278logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500279def exec_func_python(func, d, runfile, cwd=None, pythonexception=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500280 """Execute a python BB 'function'"""
281
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500282 code = _functionfmt.format(function=func)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283 bb.utils.mkdirhier(os.path.dirname(runfile))
284 with open(runfile, 'w') as script:
285 bb.data.emit_func_python(func, script, d)
286
287 if cwd:
288 try:
289 olddir = os.getcwd()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600290 except OSError as e:
291 bb.warn("%s: Cannot get cwd: %s" % (func, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500292 olddir = None
293 os.chdir(cwd)
294
295 bb.debug(2, "Executing python function %s" % func)
296
297 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500298 text = "def %s(d):\n%s" % (func, d.getVar(func, False))
299 fn = d.getVarFlag(func, "filename", False)
300 lineno = int(d.getVarFlag(func, "lineno", False))
301 bb.methodpool.insert_method(func, text, fn, lineno - 1)
302
303 comp = utils.better_compile(code, func, "exec_python_func() autogenerated")
304 utils.better_exec(comp, {"d": d}, code, "exec_python_func() autogenerated", pythonexception=pythonexception)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500305 except (bb.parse.SkipRecipe, bb.build.FuncFailed):
306 raise
Brad Bishop19323692019-04-05 15:28:33 -0400307 except Exception as e:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500308 if pythonexception:
309 raise
Brad Bishop19323692019-04-05 15:28:33 -0400310 logger.error(str(e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311 raise FuncFailed(func, None)
312 finally:
313 bb.debug(2, "Python function %s finished" % func)
314
315 if cwd and olddir:
316 try:
317 os.chdir(olddir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600318 except OSError as e:
319 bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500320
321def shell_trap_code():
322 return '''#!/bin/sh\n
323# Emit a useful diagnostic if something fails:
324bb_exit_handler() {
325 ret=$?
326 case $ret in
327 0) ;;
328 *) case $BASH_VERSION in
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500329 "") echo "WARNING: exit code $ret from a shell command.";;
330 *) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from '$BASH_COMMAND'";;
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500331 esac
332 exit $ret
333 esac
334}
335trap 'bb_exit_handler' 0
336set -e
337'''
338
339def exec_func_shell(func, d, runfile, cwd=None):
340 """Execute a shell function from the metadata
341
342 Note on directory behavior. The 'dirs' varflag should contain a list
343 of the directories you need created prior to execution. The last
344 item in the list is where we will chdir/cd to.
345 """
346
347 # Don't let the emitted shell script override PWD
348 d.delVarFlag('PWD', 'export')
349
350 with open(runfile, 'w') as script:
351 script.write(shell_trap_code())
352
353 bb.data.emit_func(func, script, d)
354
355 if bb.msg.loggerVerboseLogs:
356 script.write("set -x\n")
357 if cwd:
358 script.write("cd '%s'\n" % cwd)
359 script.write("%s\n" % func)
360 script.write('''
361# cleanup
362ret=$?
363trap '' 0
364exit $ret
365''')
366
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600367 os.chmod(runfile, 0o775)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500368
369 cmd = runfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500370 if d.getVarFlag(func, 'fakeroot', False):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500371 fakerootcmd = d.getVar('FAKEROOT')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500372 if fakerootcmd:
373 cmd = [fakerootcmd, runfile]
374
375 if bb.msg.loggerDefaultVerbose:
376 logfile = LogTee(logger, sys.stdout)
377 else:
378 logfile = sys.stdout
379
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500380 progress = d.getVarFlag(func, 'progress')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600381 if progress:
382 if progress == 'percent':
383 # Use default regex
384 logfile = bb.progress.BasicProgressHandler(d, outfile=logfile)
385 elif progress.startswith('percent:'):
386 # Use specified regex
387 logfile = bb.progress.BasicProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
388 elif progress.startswith('outof:'):
389 # Use specified regex
390 logfile = bb.progress.OutOfProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
391 else:
392 bb.warn('%s: invalid task progress varflag value "%s", ignoring' % (func, progress))
393
394 fifobuffer = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500395 def readfifo(data):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600396 nonlocal fifobuffer
397 fifobuffer.extend(data)
398 while fifobuffer:
399 message, token, nextmsg = fifobuffer.partition(b"\00")
400 if token:
401 splitval = message.split(b' ', 1)
402 cmd = splitval[0].decode("utf-8")
403 if len(splitval) > 1:
404 value = splitval[1].decode("utf-8")
405 else:
406 value = ''
407 if cmd == 'bbplain':
408 bb.plain(value)
409 elif cmd == 'bbnote':
410 bb.note(value)
411 elif cmd == 'bbwarn':
412 bb.warn(value)
413 elif cmd == 'bberror':
414 bb.error(value)
415 elif cmd == 'bbfatal':
416 # The caller will call exit themselves, so bb.error() is
417 # what we want here rather than bb.fatal()
418 bb.error(value)
419 elif cmd == 'bbfatal_log':
420 bb.error(value, forcelog=True)
421 elif cmd == 'bbdebug':
422 splitval = value.split(' ', 1)
423 level = int(splitval[0])
424 value = splitval[1]
425 bb.debug(level, value)
426 else:
427 bb.warn("Unrecognised command '%s' on FIFO" % cmd)
428 fifobuffer = nextmsg
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500429 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600430 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500431
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500432 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500433 fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
434 if os.path.exists(fifopath):
435 os.unlink(fifopath)
436 os.mkfifo(fifopath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600437 with open(fifopath, 'r+b', buffering=0) as fifo:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500438 try:
439 bb.debug(2, "Executing shell function %s" % func)
440
441 try:
442 with open(os.devnull, 'r+') as stdin:
443 bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
444 except bb.process.CmdError:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500445 logfn = d.getVar('BB_LOGFILE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446 raise FuncFailed(func, logfn)
447 finally:
448 os.unlink(fifopath)
449
450 bb.debug(2, "Shell function %s finished" % func)
451
452def _task_data(fn, task, d):
453 localdata = bb.data.createCopy(d)
454 localdata.setVar('BB_FILENAME', fn)
455 localdata.setVar('BB_CURRENTTASK', task[3:])
456 localdata.setVar('OVERRIDES', 'task-%s:%s' %
457 (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
458 localdata.finalize()
459 bb.data.expandKeys(localdata)
460 return localdata
461
462def _exec_task(fn, task, d, quieterr):
463 """Execute a BB 'task'
464
465 Execution of a task involves a bit more setup than executing a function,
466 running it with its own local metadata, and with some useful variables set.
467 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500468 if not d.getVarFlag(task, 'task', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500469 event.fire(TaskInvalid(task, d), d)
470 logger.error("No such task: %s" % task)
471 return 1
472
473 logger.debug(1, "Executing task %s", task)
474
475 localdata = _task_data(fn, task, d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500476 tempdir = localdata.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500477 if not tempdir:
478 bb.fatal("T variable not set, unable to build")
479
480 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500481 nice = localdata.getVar("BB_TASK_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500482 if nice:
483 curnice = os.nice(0)
484 nice = int(nice) - curnice
485 newnice = os.nice(nice)
486 logger.debug(1, "Renice to %s " % newnice)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500487 ionice = localdata.getVar("BB_TASK_IONICE_LEVEL")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500488 if ionice:
489 try:
490 cls, prio = ionice.split(".", 1)
491 bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
492 except:
493 bb.warn("Invalid ionice level %s" % ionice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500494
495 bb.utils.mkdirhier(tempdir)
496
497 # Determine the logfile to generate
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500498 logfmt = localdata.getVar('BB_LOGFMT') or 'log.{task}.{pid}'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500499 logbase = logfmt.format(task=task, pid=os.getpid())
500
501 # Document the order of the tasks...
502 logorder = os.path.join(tempdir, 'log.task_order')
503 try:
504 with open(logorder, 'a') as logorderfile:
505 logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
506 except OSError:
507 logger.exception("Opening log file '%s'", logorder)
508 pass
509
510 # Setup the courtesy link to the logfn
511 loglink = os.path.join(tempdir, 'log.{0}'.format(task))
512 logfn = os.path.join(tempdir, logbase)
513 if loglink:
514 bb.utils.remove(loglink)
515
516 try:
517 os.symlink(logbase, loglink)
518 except OSError:
519 pass
520
521 prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
522 postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
523
524 class ErrorCheckHandler(logging.Handler):
525 def __init__(self):
526 self.triggered = False
527 logging.Handler.__init__(self, logging.ERROR)
528 def emit(self, record):
529 if getattr(record, 'forcelog', False):
530 self.triggered = False
531 else:
532 self.triggered = True
533
534 # Handle logfiles
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500535 try:
536 bb.utils.mkdirhier(os.path.dirname(logfn))
537 logfile = open(logfn, 'w')
538 except OSError:
539 logger.exception("Opening log file '%s'", logfn)
540 pass
541
542 # Dup the existing fds so we dont lose them
543 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
544 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
545 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
546
547 # Replace those fds with our own
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800548 with open('/dev/null', 'r') as si:
549 os.dup2(si.fileno(), osi[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500550 os.dup2(logfile.fileno(), oso[1])
551 os.dup2(logfile.fileno(), ose[1])
552
553 # Ensure Python logging goes to the logfile
554 handler = logging.StreamHandler(logfile)
555 handler.setFormatter(logformatter)
556 # Always enable full debug output into task logfiles
557 handler.setLevel(logging.DEBUG - 2)
558 bblogger.addHandler(handler)
559
560 errchk = ErrorCheckHandler()
561 bblogger.addHandler(errchk)
562
563 localdata.setVar('BB_LOGFILE', logfn)
564 localdata.setVar('BB_RUNTASK', task)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500565 localdata.setVar('BB_TASK_LOGGER', bblogger)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566
567 flags = localdata.getVarFlags(task)
568
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500569 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600570 try:
571 event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
572 except (bb.BBHandledException, SystemExit):
573 return 1
574 except FuncFailed as exc:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500575 logger.error(str(exc))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600576 return 1
577
578 try:
579 for func in (prefuncs or '').split():
580 exec_func(func, localdata)
581 exec_func(task, localdata)
582 for func in (postfuncs or '').split():
583 exec_func(func, localdata)
584 except FuncFailed as exc:
585 if quieterr:
586 event.fire(TaskFailedSilent(task, logfn, localdata), localdata)
587 else:
588 errprinted = errchk.triggered
589 logger.error(str(exc))
590 event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata)
591 return 1
592 except bb.BBHandledException:
593 event.fire(TaskFailed(task, logfn, localdata, True), localdata)
594 return 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500595 finally:
596 sys.stdout.flush()
597 sys.stderr.flush()
598
599 bblogger.removeHandler(handler)
600
601 # Restore the backup fds
602 os.dup2(osi[0], osi[1])
603 os.dup2(oso[0], oso[1])
604 os.dup2(ose[0], ose[1])
605
606 # Close the backup fds
607 os.close(osi[0])
608 os.close(oso[0])
609 os.close(ose[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500610
611 logfile.close()
612 if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
613 logger.debug(2, "Zero size logfn %s, removing", logfn)
614 bb.utils.remove(logfn)
615 bb.utils.remove(loglink)
616 event.fire(TaskSucceeded(task, logfn, localdata), localdata)
617
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500618 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500619 make_stamp(task, localdata)
620
621 return 0
622
623def exec_task(fn, task, d, profile = False):
624 try:
625 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500626 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627 quieterr = True
628
629 if profile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500630 profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631 try:
632 import cProfile as profile
633 except:
634 import profile
635 prof = profile.Profile()
636 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
637 prof.dump_stats(profname)
638 bb.utils.process_profilelog(profname)
639
640 return ret
641 else:
642 return _exec_task(fn, task, d, quieterr)
643
644 except Exception:
645 from traceback import format_exc
646 if not quieterr:
647 logger.error("Build of %s failed" % (task))
648 logger.error(format_exc())
649 failedevent = TaskFailed(task, None, d, True)
650 event.fire(failedevent, d)
651 return 1
652
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600653def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654 """
655 Internal stamp helper function
656 Makes sure the stamp directory exists
657 Returns the stamp path+filename
658
659 In the bitbake core, d can be a CacheData and file_name will be set.
660 When called in task context, d will be a data store, file_name will not be set
661 """
662 taskflagname = taskname
663 if taskname.endswith("_setscene") and taskname != "do_setscene":
664 taskflagname = taskname.replace("_setscene", "")
665
666 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500667 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500668 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
669 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500670 stamp = d.getVar('STAMP')
671 file_name = d.getVar('BB_FILENAME')
672 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500673
674 if baseonly:
675 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600676 if noextra:
677 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500678
679 if not stamp:
680 return
681
682 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
683
684 stampdir = os.path.dirname(stamp)
685 if cached_mtime_noerror(stampdir) == 0:
686 bb.utils.mkdirhier(stampdir)
687
688 return stamp
689
690def stamp_cleanmask_internal(taskname, d, file_name):
691 """
692 Internal stamp helper function to generate stamp cleaning mask
693 Returns the stamp path+filename
694
695 In the bitbake core, d can be a CacheData and file_name will be set.
696 When called in task context, d will be a data store, file_name will not be set
697 """
698 taskflagname = taskname
699 if taskname.endswith("_setscene") and taskname != "do_setscene":
700 taskflagname = taskname.replace("_setscene", "")
701
702 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500703 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500704 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
705 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500706 stamp = d.getVar('STAMPCLEAN')
707 file_name = d.getVar('BB_FILENAME')
708 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500709
710 if not stamp:
711 return []
712
713 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
714
715 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
716
717def make_stamp(task, d, file_name = None):
718 """
719 Creates/updates a stamp for a given task
720 (d can be a data dict or dataCache)
721 """
722 cleanmask = stamp_cleanmask_internal(task, d, file_name)
723 for mask in cleanmask:
724 for name in glob.glob(mask):
725 # Preserve sigdata files in the stamps directory
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500726 if "sigdata" in name or "sigbasedata" in name:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727 continue
728 # Preserve taint files in the stamps directory
729 if name.endswith('.taint'):
730 continue
731 os.unlink(name)
732
733 stamp = stamp_internal(task, d, file_name)
734 # Remove the file and recreate to force timestamp
735 # change on broken NFS filesystems
736 if stamp:
737 bb.utils.remove(stamp)
738 open(stamp, "w").close()
739
740 # If we're in task context, write out a signature file for each task
741 # as it completes
742 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
743 stampbase = stamp_internal(task, d, None, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500744 file_name = d.getVar('BB_FILENAME')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500745 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
746
747def del_stamp(task, d, file_name = None):
748 """
749 Removes a stamp for a given task
750 (d can be a data dict or dataCache)
751 """
752 stamp = stamp_internal(task, d, file_name)
753 bb.utils.remove(stamp)
754
755def write_taint(task, d, file_name = None):
756 """
757 Creates a "taint" file which will force the specified task and its
758 dependents to be re-run the next time by influencing the value of its
759 taskhash.
760 (d can be a data dict or dataCache)
761 """
762 import uuid
763 if file_name:
764 taintfn = d.stamp[file_name] + '.' + task + '.taint'
765 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500766 taintfn = d.getVar('STAMP') + '.' + task + '.taint'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500767 bb.utils.mkdirhier(os.path.dirname(taintfn))
768 # The specific content of the taint file is not really important,
769 # we just need it to be random, so a random UUID is used
770 with open(taintfn, 'w') as taintf:
771 taintf.write(str(uuid.uuid4()))
772
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600773def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774 """
775 Return the stamp for a given task
776 (d can be a data dict or dataCache)
777 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600778 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779
780def add_tasks(tasklist, d):
781 task_deps = d.getVar('_task_deps', False)
782 if not task_deps:
783 task_deps = {}
784 if not 'tasks' in task_deps:
785 task_deps['tasks'] = []
786 if not 'parents' in task_deps:
787 task_deps['parents'] = {}
788
789 for task in tasklist:
790 task = d.expand(task)
791
792 d.setVarFlag(task, 'task', 1)
793
794 if not task in task_deps['tasks']:
795 task_deps['tasks'].append(task)
796
797 flags = d.getVarFlags(task)
798 def getTask(name):
799 if not name in task_deps:
800 task_deps[name] = {}
801 if name in flags:
802 deptask = d.expand(flags[name])
803 task_deps[name][task] = deptask
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800804 getTask('mcdepends')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500805 getTask('depends')
806 getTask('rdepends')
807 getTask('deptask')
808 getTask('rdeptask')
809 getTask('recrdeptask')
810 getTask('recideptask')
811 getTask('nostamp')
812 getTask('fakeroot')
813 getTask('noexec')
814 getTask('umask')
815 task_deps['parents'][task] = []
816 if 'deps' in flags:
817 for dep in flags['deps']:
818 dep = d.expand(dep)
819 task_deps['parents'][task].append(dep)
820
821 # don't assume holding a reference
822 d.setVar('_task_deps', task_deps)
823
824def addtask(task, before, after, d):
825 if task[:3] != "do_":
826 task = "do_" + task
827
828 d.setVarFlag(task, "task", 1)
829 bbtasks = d.getVar('__BBTASKS', False) or []
830 if task not in bbtasks:
831 bbtasks.append(task)
832 d.setVar('__BBTASKS', bbtasks)
833
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500834 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500835 if after is not None:
836 # set up deps for function
837 for entry in after.split():
838 if entry not in existing:
839 existing.append(entry)
840 d.setVarFlag(task, "deps", existing)
841 if before is not None:
842 # set up things that depend on this func
843 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500844 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500845 if task not in existing:
846 d.setVarFlag(entry, "deps", [task] + existing)
847
848def deltask(task, d):
849 if task[:3] != "do_":
850 task = "do_" + task
851
852 bbtasks = d.getVar('__BBTASKS', False) or []
853 if task in bbtasks:
854 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600855 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500856 d.setVar('__BBTASKS', bbtasks)
857
858 d.delVarFlag(task, 'deps')
859 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500860 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500861 if task in deps:
862 deps.remove(task)
863 d.setVarFlag(bbtask, 'deps', deps)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500864
865def preceedtask(task, with_recrdeptasks, d):
866 """
867 Returns a set of tasks in the current recipe which were specified as
868 precondition by the task itself ("after") or which listed themselves
869 as precondition ("before"). Preceeding tasks specified via the
870 "recrdeptask" are included in the result only if requested. Beware
871 that this may lead to the task itself being listed.
872 """
873 preceed = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400874
875 # Ignore tasks which don't exist
876 tasks = d.getVar('__BBTASKS', False)
877 if task not in tasks:
878 return preceed
879
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500880 preceed.update(d.getVarFlag(task, 'deps') or [])
881 if with_recrdeptasks:
882 recrdeptask = d.getVarFlag(task, 'recrdeptask')
883 if recrdeptask:
884 preceed.update(recrdeptask.split())
885 return preceed
886
887def tasksbetween(task_start, task_end, d):
888 """
889 Return the list of tasks between two tasks in the current recipe,
890 where task_start is to start at and task_end is the task to end at
891 (and task_end has a dependency chain back to task_start).
892 """
893 outtasks = []
894 tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
895 def follow_chain(task, endtask, chain=None):
896 if not chain:
897 chain = []
898 chain.append(task)
899 for othertask in tasks:
900 if othertask == task:
901 continue
902 if task == endtask:
903 for ctask in chain:
904 if ctask not in outtasks:
905 outtasks.append(ctask)
906 else:
907 deps = d.getVarFlag(othertask, 'deps', False)
908 if task in deps:
909 follow_chain(othertask, endtask, chain)
910 chain.pop()
911 follow_chain(task_start, task_end)
912 return outtasks