blob: 4631abdde573c391c9ade1765481d3c230a18db6 [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
44NULL = open(os.devnull, 'r+')
45
46__mtime_cache = {}
47
48def cached_mtime_noerror(f):
49 if f not in __mtime_cache:
50 try:
51 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
52 except OSError:
53 return 0
54 return __mtime_cache[f]
55
56def reset_cache():
57 global __mtime_cache
58 __mtime_cache = {}
59
60# When we execute a Python function, we'd like certain things
61# in all namespaces, hence we add them to __builtins__.
62# If we do not do this and use the exec globals, they will
63# not be available to subfunctions.
Patrick Williamsc0f7c042017-02-23 20:41:17 -060064if hasattr(__builtins__, '__setitem__'):
65 builtins = __builtins__
66else:
67 builtins = __builtins__.__dict__
68
69builtins['bb'] = bb
70builtins['os'] = os
Patrick Williamsc124f4f2015-09-15 14:41:29 -050071
72class FuncFailed(Exception):
73 def __init__(self, name = None, logfile = None):
74 self.logfile = logfile
75 self.name = name
76 if name:
77 self.msg = 'Function failed: %s' % name
78 else:
79 self.msg = "Function failed"
80
81 def __str__(self):
82 if self.logfile and os.path.exists(self.logfile):
83 msg = ("%s (log file is located at %s)" %
84 (self.msg, self.logfile))
85 else:
86 msg = self.msg
87 return msg
88
89class TaskBase(event.Event):
90 """Base class for task events"""
91
92 def __init__(self, t, logfile, d):
93 self._task = t
Brad Bishop6e60e8b2018-02-01 10:27:11 -050094 self._package = d.getVar("PF")
95 self._mc = d.getVar("BB_CURRENT_MC")
96 self.taskfile = d.getVar("FILE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050097 self.taskname = self._task
98 self.logfile = logfile
99 self.time = time.time()
100 event.Event.__init__(self)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500101 self._message = "recipe %s: task %s: %s" % (d.getVar("PF"), t, self.getDisplayName())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102
103 def getTask(self):
104 return self._task
105
106 def setTask(self, task):
107 self._task = task
108
109 def getDisplayName(self):
110 return bb.event.getName(self)[4:]
111
112 task = property(getTask, setTask, None, "task property")
113
114class TaskStarted(TaskBase):
115 """Task execution started"""
116 def __init__(self, t, logfile, taskflags, d):
117 super(TaskStarted, self).__init__(t, logfile, d)
118 self.taskflags = taskflags
119
120class TaskSucceeded(TaskBase):
121 """Task execution completed"""
122
123class TaskFailed(TaskBase):
124 """Task execution failed"""
125
126 def __init__(self, task, logfile, metadata, errprinted = False):
127 self.errprinted = errprinted
128 super(TaskFailed, self).__init__(task, logfile, metadata)
129
130class TaskFailedSilent(TaskBase):
131 """Task execution failed (silently)"""
132 def getDisplayName(self):
133 # Don't need to tell the user it was silent
134 return "Failed"
135
136class TaskInvalid(TaskBase):
137
138 def __init__(self, task, metadata):
139 super(TaskInvalid, self).__init__(task, None, metadata)
140 self._message = "No such task '%s'" % task
141
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600142class TaskProgress(event.Event):
143 """
144 Task made some progress that could be reported to the user, usually in
145 the form of a progress bar or similar.
146 NOTE: this class does not inherit from TaskBase since it doesn't need
147 to - it's fired within the task context itself, so we don't have any of
148 the context information that you do in the case of the other events.
149 The event PID can be used to determine which task it came from.
150 The progress value is normally 0-100, but can also be negative
151 indicating that progress has been made but we aren't able to determine
152 how much.
153 The rate is optional, this is simply an extra string to display to the
154 user if specified.
155 """
156 def __init__(self, progress, rate=None):
157 self.progress = progress
158 self.rate = rate
159 event.Event.__init__(self)
160
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500161
162class LogTee(object):
163 def __init__(self, logger, outfile):
164 self.outfile = outfile
165 self.logger = logger
166 self.name = self.outfile.name
167
168 def write(self, string):
169 self.logger.plain(string)
170 self.outfile.write(string)
171
172 def __enter__(self):
173 self.outfile.__enter__()
174 return self
175
176 def __exit__(self, *excinfo):
177 self.outfile.__exit__(*excinfo)
178
179 def __repr__(self):
180 return '<LogTee {0}>'.format(self.name)
181 def flush(self):
182 self.outfile.flush()
183
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500184#
185# pythonexception allows the python exceptions generated to be raised
186# as the real exceptions (not FuncFailed) and without a backtrace at the
187# origin of the failure.
188#
189def exec_func(func, d, dirs = None, pythonexception=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500190 """Execute a BB 'function'"""
191
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600192 try:
193 oldcwd = os.getcwd()
194 except:
195 oldcwd = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500196
197 flags = d.getVarFlags(func)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500198 cleandirs = flags.get('cleandirs') if flags else None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500199 if cleandirs:
200 for cdir in d.expand(cleandirs).split():
201 bb.utils.remove(cdir, True)
202 bb.utils.mkdirhier(cdir)
203
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500204 if flags and dirs is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500205 dirs = flags.get('dirs')
206 if dirs:
207 dirs = d.expand(dirs).split()
208
209 if dirs:
210 for adir in dirs:
211 bb.utils.mkdirhier(adir)
212 adir = dirs[-1]
213 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600214 adir = None
215
216 body = d.getVar(func, False)
217 if not body:
218 if body is None:
219 logger.warning("Function %s doesn't exist", func)
220 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221
222 ispython = flags.get('python')
223
224 lockflag = flags.get('lockfiles')
225 if lockflag:
226 lockfiles = [f for f in d.expand(lockflag).split()]
227 else:
228 lockfiles = None
229
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500230 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500231
232 # or func allows items to be executed outside of the normal
233 # task set, such as buildhistory
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500234 task = d.getVar('BB_RUNTASK') or func
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500235 if task == func:
236 taskfunc = task
237 else:
238 taskfunc = "%s.%s" % (task, func)
239
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500240 runfmt = d.getVar('BB_RUNFMT') or "run.{func}.{pid}"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500241 runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
242 runfile = os.path.join(tempdir, runfn)
243 bb.utils.mkdirhier(os.path.dirname(runfile))
244
245 # Setup the courtesy link to the runfn, only for tasks
246 # we create the link 'just' before the run script is created
247 # if we create it after, and if the run script fails, then the
248 # link won't be created as an exception would be fired.
249 if task == func:
250 runlink = os.path.join(tempdir, 'run.{0}'.format(task))
251 if runlink:
252 bb.utils.remove(runlink)
253
254 try:
255 os.symlink(runfn, runlink)
256 except OSError:
257 pass
258
259 with bb.utils.fileslocked(lockfiles):
260 if ispython:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500261 exec_func_python(func, d, runfile, cwd=adir, pythonexception=pythonexception)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262 else:
263 exec_func_shell(func, d, runfile, cwd=adir)
264
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600265 try:
266 curcwd = os.getcwd()
267 except:
268 curcwd = None
269
270 if oldcwd and curcwd != oldcwd:
271 try:
272 bb.warn("Task %s changed cwd to %s" % (func, curcwd))
273 os.chdir(oldcwd)
274 except:
275 pass
276
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500277_functionfmt = """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500278{function}(d)
279"""
280logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500281def exec_func_python(func, d, runfile, cwd=None, pythonexception=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500282 """Execute a python BB 'function'"""
283
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500284 code = _functionfmt.format(function=func)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500285 bb.utils.mkdirhier(os.path.dirname(runfile))
286 with open(runfile, 'w') as script:
287 bb.data.emit_func_python(func, script, d)
288
289 if cwd:
290 try:
291 olddir = os.getcwd()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600292 except OSError as e:
293 bb.warn("%s: Cannot get cwd: %s" % (func, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500294 olddir = None
295 os.chdir(cwd)
296
297 bb.debug(2, "Executing python function %s" % func)
298
299 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500300 text = "def %s(d):\n%s" % (func, d.getVar(func, False))
301 fn = d.getVarFlag(func, "filename", False)
302 lineno = int(d.getVarFlag(func, "lineno", False))
303 bb.methodpool.insert_method(func, text, fn, lineno - 1)
304
305 comp = utils.better_compile(code, func, "exec_python_func() autogenerated")
306 utils.better_exec(comp, {"d": d}, code, "exec_python_func() autogenerated", pythonexception=pythonexception)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500307 except (bb.parse.SkipRecipe, bb.build.FuncFailed):
308 raise
309 except:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500310 if pythonexception:
311 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500312 raise FuncFailed(func, None)
313 finally:
314 bb.debug(2, "Python function %s finished" % func)
315
316 if cwd and olddir:
317 try:
318 os.chdir(olddir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600319 except OSError as e:
320 bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321
322def shell_trap_code():
323 return '''#!/bin/sh\n
324# Emit a useful diagnostic if something fails:
325bb_exit_handler() {
326 ret=$?
327 case $ret in
328 0) ;;
329 *) case $BASH_VERSION in
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500330 "") echo "WARNING: exit code $ret from a shell command.";;
331 *) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from '$BASH_COMMAND'";;
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500332 esac
333 exit $ret
334 esac
335}
336trap 'bb_exit_handler' 0
337set -e
338'''
339
340def exec_func_shell(func, d, runfile, cwd=None):
341 """Execute a shell function from the metadata
342
343 Note on directory behavior. The 'dirs' varflag should contain a list
344 of the directories you need created prior to execution. The last
345 item in the list is where we will chdir/cd to.
346 """
347
348 # Don't let the emitted shell script override PWD
349 d.delVarFlag('PWD', 'export')
350
351 with open(runfile, 'w') as script:
352 script.write(shell_trap_code())
353
354 bb.data.emit_func(func, script, d)
355
356 if bb.msg.loggerVerboseLogs:
357 script.write("set -x\n")
358 if cwd:
359 script.write("cd '%s'\n" % cwd)
360 script.write("%s\n" % func)
361 script.write('''
362# cleanup
363ret=$?
364trap '' 0
365exit $ret
366''')
367
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600368 os.chmod(runfile, 0o775)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500369
370 cmd = runfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500371 if d.getVarFlag(func, 'fakeroot', False):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500372 fakerootcmd = d.getVar('FAKEROOT')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500373 if fakerootcmd:
374 cmd = [fakerootcmd, runfile]
375
376 if bb.msg.loggerDefaultVerbose:
377 logfile = LogTee(logger, sys.stdout)
378 else:
379 logfile = sys.stdout
380
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500381 progress = d.getVarFlag(func, 'progress')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600382 if progress:
383 if progress == 'percent':
384 # Use default regex
385 logfile = bb.progress.BasicProgressHandler(d, outfile=logfile)
386 elif progress.startswith('percent:'):
387 # Use specified regex
388 logfile = bb.progress.BasicProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
389 elif progress.startswith('outof:'):
390 # Use specified regex
391 logfile = bb.progress.OutOfProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
392 else:
393 bb.warn('%s: invalid task progress varflag value "%s", ignoring' % (func, progress))
394
395 fifobuffer = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500396 def readfifo(data):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600397 nonlocal fifobuffer
398 fifobuffer.extend(data)
399 while fifobuffer:
400 message, token, nextmsg = fifobuffer.partition(b"\00")
401 if token:
402 splitval = message.split(b' ', 1)
403 cmd = splitval[0].decode("utf-8")
404 if len(splitval) > 1:
405 value = splitval[1].decode("utf-8")
406 else:
407 value = ''
408 if cmd == 'bbplain':
409 bb.plain(value)
410 elif cmd == 'bbnote':
411 bb.note(value)
412 elif cmd == 'bbwarn':
413 bb.warn(value)
414 elif cmd == 'bberror':
415 bb.error(value)
416 elif cmd == 'bbfatal':
417 # The caller will call exit themselves, so bb.error() is
418 # what we want here rather than bb.fatal()
419 bb.error(value)
420 elif cmd == 'bbfatal_log':
421 bb.error(value, forcelog=True)
422 elif cmd == 'bbdebug':
423 splitval = value.split(' ', 1)
424 level = int(splitval[0])
425 value = splitval[1]
426 bb.debug(level, value)
427 else:
428 bb.warn("Unrecognised command '%s' on FIFO" % cmd)
429 fifobuffer = nextmsg
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500430 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600431 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500432
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500433 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500434 fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
435 if os.path.exists(fifopath):
436 os.unlink(fifopath)
437 os.mkfifo(fifopath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600438 with open(fifopath, 'r+b', buffering=0) as fifo:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500439 try:
440 bb.debug(2, "Executing shell function %s" % func)
441
442 try:
443 with open(os.devnull, 'r+') as stdin:
444 bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
445 except bb.process.CmdError:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500446 logfn = d.getVar('BB_LOGFILE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500447 raise FuncFailed(func, logfn)
448 finally:
449 os.unlink(fifopath)
450
451 bb.debug(2, "Shell function %s finished" % func)
452
453def _task_data(fn, task, d):
454 localdata = bb.data.createCopy(d)
455 localdata.setVar('BB_FILENAME', fn)
456 localdata.setVar('BB_CURRENTTASK', task[3:])
457 localdata.setVar('OVERRIDES', 'task-%s:%s' %
458 (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
459 localdata.finalize()
460 bb.data.expandKeys(localdata)
461 return localdata
462
463def _exec_task(fn, task, d, quieterr):
464 """Execute a BB 'task'
465
466 Execution of a task involves a bit more setup than executing a function,
467 running it with its own local metadata, and with some useful variables set.
468 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500469 if not d.getVarFlag(task, 'task', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500470 event.fire(TaskInvalid(task, d), d)
471 logger.error("No such task: %s" % task)
472 return 1
473
474 logger.debug(1, "Executing task %s", task)
475
476 localdata = _task_data(fn, task, d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500477 tempdir = localdata.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500478 if not tempdir:
479 bb.fatal("T variable not set, unable to build")
480
481 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500482 nice = localdata.getVar("BB_TASK_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500483 if nice:
484 curnice = os.nice(0)
485 nice = int(nice) - curnice
486 newnice = os.nice(nice)
487 logger.debug(1, "Renice to %s " % newnice)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500488 ionice = localdata.getVar("BB_TASK_IONICE_LEVEL")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500489 if ionice:
490 try:
491 cls, prio = ionice.split(".", 1)
492 bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
493 except:
494 bb.warn("Invalid ionice level %s" % ionice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500495
496 bb.utils.mkdirhier(tempdir)
497
498 # Determine the logfile to generate
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500499 logfmt = localdata.getVar('BB_LOGFMT') or 'log.{task}.{pid}'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500500 logbase = logfmt.format(task=task, pid=os.getpid())
501
502 # Document the order of the tasks...
503 logorder = os.path.join(tempdir, 'log.task_order')
504 try:
505 with open(logorder, 'a') as logorderfile:
506 logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
507 except OSError:
508 logger.exception("Opening log file '%s'", logorder)
509 pass
510
511 # Setup the courtesy link to the logfn
512 loglink = os.path.join(tempdir, 'log.{0}'.format(task))
513 logfn = os.path.join(tempdir, logbase)
514 if loglink:
515 bb.utils.remove(loglink)
516
517 try:
518 os.symlink(logbase, loglink)
519 except OSError:
520 pass
521
522 prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
523 postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
524
525 class ErrorCheckHandler(logging.Handler):
526 def __init__(self):
527 self.triggered = False
528 logging.Handler.__init__(self, logging.ERROR)
529 def emit(self, record):
530 if getattr(record, 'forcelog', False):
531 self.triggered = False
532 else:
533 self.triggered = True
534
535 # Handle logfiles
536 si = open('/dev/null', 'r')
537 try:
538 bb.utils.mkdirhier(os.path.dirname(logfn))
539 logfile = open(logfn, 'w')
540 except OSError:
541 logger.exception("Opening log file '%s'", logfn)
542 pass
543
544 # Dup the existing fds so we dont lose them
545 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
546 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
547 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
548
549 # Replace those fds with our own
550 os.dup2(si.fileno(), osi[1])
551 os.dup2(logfile.fileno(), oso[1])
552 os.dup2(logfile.fileno(), ose[1])
553
554 # Ensure Python logging goes to the logfile
555 handler = logging.StreamHandler(logfile)
556 handler.setFormatter(logformatter)
557 # Always enable full debug output into task logfiles
558 handler.setLevel(logging.DEBUG - 2)
559 bblogger.addHandler(handler)
560
561 errchk = ErrorCheckHandler()
562 bblogger.addHandler(errchk)
563
564 localdata.setVar('BB_LOGFILE', logfn)
565 localdata.setVar('BB_RUNTASK', task)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500566 localdata.setVar('BB_TASK_LOGGER', bblogger)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500567
568 flags = localdata.getVarFlags(task)
569
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500570 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600571 try:
572 event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
573 except (bb.BBHandledException, SystemExit):
574 return 1
575 except FuncFailed as exc:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500576 logger.error(str(exc))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600577 return 1
578
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)
585 except FuncFailed as exc:
586 if quieterr:
587 event.fire(TaskFailedSilent(task, logfn, localdata), localdata)
588 else:
589 errprinted = errchk.triggered
590 logger.error(str(exc))
591 event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata)
592 return 1
593 except bb.BBHandledException:
594 event.fire(TaskFailed(task, logfn, localdata, True), 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])
611 si.close()
612
613 logfile.close()
614 if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
615 logger.debug(2, "Zero size logfn %s, removing", logfn)
616 bb.utils.remove(logfn)
617 bb.utils.remove(loglink)
618 event.fire(TaskSucceeded(task, logfn, localdata), localdata)
619
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500620 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500621 make_stamp(task, localdata)
622
623 return 0
624
625def exec_task(fn, task, d, profile = False):
626 try:
627 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500628 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500629 quieterr = True
630
631 if profile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500632 profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500633 try:
634 import cProfile as profile
635 except:
636 import profile
637 prof = profile.Profile()
638 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
639 prof.dump_stats(profname)
640 bb.utils.process_profilelog(profname)
641
642 return ret
643 else:
644 return _exec_task(fn, task, d, quieterr)
645
646 except Exception:
647 from traceback import format_exc
648 if not quieterr:
649 logger.error("Build of %s failed" % (task))
650 logger.error(format_exc())
651 failedevent = TaskFailed(task, None, d, True)
652 event.fire(failedevent, d)
653 return 1
654
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600655def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656 """
657 Internal stamp helper function
658 Makes sure the stamp directory exists
659 Returns the stamp path+filename
660
661 In the bitbake core, d can be a CacheData and file_name will be set.
662 When called in task context, d will be a data store, file_name will not be set
663 """
664 taskflagname = taskname
665 if taskname.endswith("_setscene") and taskname != "do_setscene":
666 taskflagname = taskname.replace("_setscene", "")
667
668 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500669 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500670 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
671 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500672 stamp = d.getVar('STAMP')
673 file_name = d.getVar('BB_FILENAME')
674 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500675
676 if baseonly:
677 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600678 if noextra:
679 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500680
681 if not stamp:
682 return
683
684 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
685
686 stampdir = os.path.dirname(stamp)
687 if cached_mtime_noerror(stampdir) == 0:
688 bb.utils.mkdirhier(stampdir)
689
690 return stamp
691
692def stamp_cleanmask_internal(taskname, d, file_name):
693 """
694 Internal stamp helper function to generate stamp cleaning mask
695 Returns the stamp path+filename
696
697 In the bitbake core, d can be a CacheData and file_name will be set.
698 When called in task context, d will be a data store, file_name will not be set
699 """
700 taskflagname = taskname
701 if taskname.endswith("_setscene") and taskname != "do_setscene":
702 taskflagname = taskname.replace("_setscene", "")
703
704 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500705 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500706 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
707 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500708 stamp = d.getVar('STAMPCLEAN')
709 file_name = d.getVar('BB_FILENAME')
710 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500711
712 if not stamp:
713 return []
714
715 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
716
717 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
718
719def make_stamp(task, d, file_name = None):
720 """
721 Creates/updates a stamp for a given task
722 (d can be a data dict or dataCache)
723 """
724 cleanmask = stamp_cleanmask_internal(task, d, file_name)
725 for mask in cleanmask:
726 for name in glob.glob(mask):
727 # Preserve sigdata files in the stamps directory
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500728 if "sigdata" in name or "sigbasedata" in name:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500729 continue
730 # Preserve taint files in the stamps directory
731 if name.endswith('.taint'):
732 continue
733 os.unlink(name)
734
735 stamp = stamp_internal(task, d, file_name)
736 # Remove the file and recreate to force timestamp
737 # change on broken NFS filesystems
738 if stamp:
739 bb.utils.remove(stamp)
740 open(stamp, "w").close()
741
742 # If we're in task context, write out a signature file for each task
743 # as it completes
744 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
745 stampbase = stamp_internal(task, d, None, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500746 file_name = d.getVar('BB_FILENAME')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500747 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
748
749def del_stamp(task, d, file_name = None):
750 """
751 Removes a stamp for a given task
752 (d can be a data dict or dataCache)
753 """
754 stamp = stamp_internal(task, d, file_name)
755 bb.utils.remove(stamp)
756
757def write_taint(task, d, file_name = None):
758 """
759 Creates a "taint" file which will force the specified task and its
760 dependents to be re-run the next time by influencing the value of its
761 taskhash.
762 (d can be a data dict or dataCache)
763 """
764 import uuid
765 if file_name:
766 taintfn = d.stamp[file_name] + '.' + task + '.taint'
767 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500768 taintfn = d.getVar('STAMP') + '.' + task + '.taint'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500769 bb.utils.mkdirhier(os.path.dirname(taintfn))
770 # The specific content of the taint file is not really important,
771 # we just need it to be random, so a random UUID is used
772 with open(taintfn, 'w') as taintf:
773 taintf.write(str(uuid.uuid4()))
774
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600775def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500776 """
777 Return the stamp for a given task
778 (d can be a data dict or dataCache)
779 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600780 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500781
782def add_tasks(tasklist, d):
783 task_deps = d.getVar('_task_deps', False)
784 if not task_deps:
785 task_deps = {}
786 if not 'tasks' in task_deps:
787 task_deps['tasks'] = []
788 if not 'parents' in task_deps:
789 task_deps['parents'] = {}
790
791 for task in tasklist:
792 task = d.expand(task)
793
794 d.setVarFlag(task, 'task', 1)
795
796 if not task in task_deps['tasks']:
797 task_deps['tasks'].append(task)
798
799 flags = d.getVarFlags(task)
800 def getTask(name):
801 if not name in task_deps:
802 task_deps[name] = {}
803 if name in flags:
804 deptask = d.expand(flags[name])
805 task_deps[name][task] = deptask
806 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']:
819 dep = d.expand(dep)
820 task_deps['parents'][task].append(dep)
821
822 # don't assume holding a reference
823 d.setVar('_task_deps', task_deps)
824
825def addtask(task, before, after, d):
826 if task[:3] != "do_":
827 task = "do_" + task
828
829 d.setVarFlag(task, "task", 1)
830 bbtasks = d.getVar('__BBTASKS', False) or []
831 if task not in bbtasks:
832 bbtasks.append(task)
833 d.setVar('__BBTASKS', bbtasks)
834
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500835 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500836 if after is not None:
837 # set up deps for function
838 for entry in after.split():
839 if entry not in existing:
840 existing.append(entry)
841 d.setVarFlag(task, "deps", existing)
842 if before is not None:
843 # set up things that depend on this func
844 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500845 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500846 if task not in existing:
847 d.setVarFlag(entry, "deps", [task] + existing)
848
849def deltask(task, d):
850 if task[:3] != "do_":
851 task = "do_" + task
852
853 bbtasks = d.getVar('__BBTASKS', False) or []
854 if task in bbtasks:
855 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600856 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500857 d.setVar('__BBTASKS', bbtasks)
858
859 d.delVarFlag(task, 'deps')
860 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500861 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500862 if task in deps:
863 deps.remove(task)
864 d.setVarFlag(bbtask, 'deps', deps)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500865
866def preceedtask(task, with_recrdeptasks, d):
867 """
868 Returns a set of tasks in the current recipe which were specified as
869 precondition by the task itself ("after") or which listed themselves
870 as precondition ("before"). Preceeding tasks specified via the
871 "recrdeptask" are included in the result only if requested. Beware
872 that this may lead to the task itself being listed.
873 """
874 preceed = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400875
876 # Ignore tasks which don't exist
877 tasks = d.getVar('__BBTASKS', False)
878 if task not in tasks:
879 return preceed
880
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500881 preceed.update(d.getVarFlag(task, 'deps') or [])
882 if with_recrdeptasks:
883 recrdeptask = d.getVarFlag(task, 'recrdeptask')
884 if recrdeptask:
885 preceed.update(recrdeptask.split())
886 return preceed
887
888def tasksbetween(task_start, task_end, d):
889 """
890 Return the list of tasks between two tasks in the current recipe,
891 where task_start is to start at and task_end is the task to end at
892 (and task_end has a dependency chain back to task_start).
893 """
894 outtasks = []
895 tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
896 def follow_chain(task, endtask, chain=None):
897 if not chain:
898 chain = []
899 chain.append(task)
900 for othertask in tasks:
901 if othertask == task:
902 continue
903 if task == endtask:
904 for ctask in chain:
905 if ctask not in outtasks:
906 outtasks.append(ctask)
907 else:
908 deps = d.getVarFlag(othertask, 'deps', False)
909 if task in deps:
910 follow_chain(othertask, endtask, chain)
911 chain.pop()
912 follow_chain(task_start, task_end)
913 return outtasks