blob: 3e2a94edb11af97adac0e4d2a92daf8382df4ca4 [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
307 except:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500308 if pythonexception:
309 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500310 raise FuncFailed(func, None)
311 finally:
312 bb.debug(2, "Python function %s finished" % func)
313
314 if cwd and olddir:
315 try:
316 os.chdir(olddir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600317 except OSError as e:
318 bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500319
320def shell_trap_code():
321 return '''#!/bin/sh\n
322# Emit a useful diagnostic if something fails:
323bb_exit_handler() {
324 ret=$?
325 case $ret in
326 0) ;;
327 *) case $BASH_VERSION in
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500328 "") echo "WARNING: exit code $ret from a shell command.";;
329 *) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from '$BASH_COMMAND'";;
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500330 esac
331 exit $ret
332 esac
333}
334trap 'bb_exit_handler' 0
335set -e
336'''
337
338def exec_func_shell(func, d, runfile, cwd=None):
339 """Execute a shell function from the metadata
340
341 Note on directory behavior. The 'dirs' varflag should contain a list
342 of the directories you need created prior to execution. The last
343 item in the list is where we will chdir/cd to.
344 """
345
346 # Don't let the emitted shell script override PWD
347 d.delVarFlag('PWD', 'export')
348
349 with open(runfile, 'w') as script:
350 script.write(shell_trap_code())
351
352 bb.data.emit_func(func, script, d)
353
354 if bb.msg.loggerVerboseLogs:
355 script.write("set -x\n")
356 if cwd:
357 script.write("cd '%s'\n" % cwd)
358 script.write("%s\n" % func)
359 script.write('''
360# cleanup
361ret=$?
362trap '' 0
363exit $ret
364''')
365
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600366 os.chmod(runfile, 0o775)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500367
368 cmd = runfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500369 if d.getVarFlag(func, 'fakeroot', False):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500370 fakerootcmd = d.getVar('FAKEROOT')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500371 if fakerootcmd:
372 cmd = [fakerootcmd, runfile]
373
374 if bb.msg.loggerDefaultVerbose:
375 logfile = LogTee(logger, sys.stdout)
376 else:
377 logfile = sys.stdout
378
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500379 progress = d.getVarFlag(func, 'progress')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600380 if progress:
381 if progress == 'percent':
382 # Use default regex
383 logfile = bb.progress.BasicProgressHandler(d, outfile=logfile)
384 elif progress.startswith('percent:'):
385 # Use specified regex
386 logfile = bb.progress.BasicProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
387 elif progress.startswith('outof:'):
388 # Use specified regex
389 logfile = bb.progress.OutOfProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
390 else:
391 bb.warn('%s: invalid task progress varflag value "%s", ignoring' % (func, progress))
392
393 fifobuffer = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500394 def readfifo(data):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600395 nonlocal fifobuffer
396 fifobuffer.extend(data)
397 while fifobuffer:
398 message, token, nextmsg = fifobuffer.partition(b"\00")
399 if token:
400 splitval = message.split(b' ', 1)
401 cmd = splitval[0].decode("utf-8")
402 if len(splitval) > 1:
403 value = splitval[1].decode("utf-8")
404 else:
405 value = ''
406 if cmd == 'bbplain':
407 bb.plain(value)
408 elif cmd == 'bbnote':
409 bb.note(value)
410 elif cmd == 'bbwarn':
411 bb.warn(value)
412 elif cmd == 'bberror':
413 bb.error(value)
414 elif cmd == 'bbfatal':
415 # The caller will call exit themselves, so bb.error() is
416 # what we want here rather than bb.fatal()
417 bb.error(value)
418 elif cmd == 'bbfatal_log':
419 bb.error(value, forcelog=True)
420 elif cmd == 'bbdebug':
421 splitval = value.split(' ', 1)
422 level = int(splitval[0])
423 value = splitval[1]
424 bb.debug(level, value)
425 else:
426 bb.warn("Unrecognised command '%s' on FIFO" % cmd)
427 fifobuffer = nextmsg
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500428 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600429 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500430
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500431 tempdir = d.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500432 fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
433 if os.path.exists(fifopath):
434 os.unlink(fifopath)
435 os.mkfifo(fifopath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600436 with open(fifopath, 'r+b', buffering=0) as fifo:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500437 try:
438 bb.debug(2, "Executing shell function %s" % func)
439
440 try:
441 with open(os.devnull, 'r+') as stdin:
442 bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
443 except bb.process.CmdError:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500444 logfn = d.getVar('BB_LOGFILE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500445 raise FuncFailed(func, logfn)
446 finally:
447 os.unlink(fifopath)
448
449 bb.debug(2, "Shell function %s finished" % func)
450
451def _task_data(fn, task, d):
452 localdata = bb.data.createCopy(d)
453 localdata.setVar('BB_FILENAME', fn)
454 localdata.setVar('BB_CURRENTTASK', task[3:])
455 localdata.setVar('OVERRIDES', 'task-%s:%s' %
456 (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
457 localdata.finalize()
458 bb.data.expandKeys(localdata)
459 return localdata
460
461def _exec_task(fn, task, d, quieterr):
462 """Execute a BB 'task'
463
464 Execution of a task involves a bit more setup than executing a function,
465 running it with its own local metadata, and with some useful variables set.
466 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500467 if not d.getVarFlag(task, 'task', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500468 event.fire(TaskInvalid(task, d), d)
469 logger.error("No such task: %s" % task)
470 return 1
471
472 logger.debug(1, "Executing task %s", task)
473
474 localdata = _task_data(fn, task, d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500475 tempdir = localdata.getVar('T')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500476 if not tempdir:
477 bb.fatal("T variable not set, unable to build")
478
479 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500480 nice = localdata.getVar("BB_TASK_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500481 if nice:
482 curnice = os.nice(0)
483 nice = int(nice) - curnice
484 newnice = os.nice(nice)
485 logger.debug(1, "Renice to %s " % newnice)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500486 ionice = localdata.getVar("BB_TASK_IONICE_LEVEL")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500487 if ionice:
488 try:
489 cls, prio = ionice.split(".", 1)
490 bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
491 except:
492 bb.warn("Invalid ionice level %s" % ionice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500493
494 bb.utils.mkdirhier(tempdir)
495
496 # Determine the logfile to generate
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500497 logfmt = localdata.getVar('BB_LOGFMT') or 'log.{task}.{pid}'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500498 logbase = logfmt.format(task=task, pid=os.getpid())
499
500 # Document the order of the tasks...
501 logorder = os.path.join(tempdir, 'log.task_order')
502 try:
503 with open(logorder, 'a') as logorderfile:
504 logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
505 except OSError:
506 logger.exception("Opening log file '%s'", logorder)
507 pass
508
509 # Setup the courtesy link to the logfn
510 loglink = os.path.join(tempdir, 'log.{0}'.format(task))
511 logfn = os.path.join(tempdir, logbase)
512 if loglink:
513 bb.utils.remove(loglink)
514
515 try:
516 os.symlink(logbase, loglink)
517 except OSError:
518 pass
519
520 prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
521 postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
522
523 class ErrorCheckHandler(logging.Handler):
524 def __init__(self):
525 self.triggered = False
526 logging.Handler.__init__(self, logging.ERROR)
527 def emit(self, record):
528 if getattr(record, 'forcelog', False):
529 self.triggered = False
530 else:
531 self.triggered = True
532
533 # Handle logfiles
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534 try:
535 bb.utils.mkdirhier(os.path.dirname(logfn))
536 logfile = open(logfn, 'w')
537 except OSError:
538 logger.exception("Opening log file '%s'", logfn)
539 pass
540
541 # Dup the existing fds so we dont lose them
542 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
543 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
544 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
545
546 # Replace those fds with our own
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800547 with open('/dev/null', 'r') as si:
548 os.dup2(si.fileno(), osi[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500549 os.dup2(logfile.fileno(), oso[1])
550 os.dup2(logfile.fileno(), ose[1])
551
552 # Ensure Python logging goes to the logfile
553 handler = logging.StreamHandler(logfile)
554 handler.setFormatter(logformatter)
555 # Always enable full debug output into task logfiles
556 handler.setLevel(logging.DEBUG - 2)
557 bblogger.addHandler(handler)
558
559 errchk = ErrorCheckHandler()
560 bblogger.addHandler(errchk)
561
562 localdata.setVar('BB_LOGFILE', logfn)
563 localdata.setVar('BB_RUNTASK', task)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500564 localdata.setVar('BB_TASK_LOGGER', bblogger)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500565
566 flags = localdata.getVarFlags(task)
567
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500568 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600569 try:
570 event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
571 except (bb.BBHandledException, SystemExit):
572 return 1
573 except FuncFailed as exc:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500574 logger.error(str(exc))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600575 return 1
576
577 try:
578 for func in (prefuncs or '').split():
579 exec_func(func, localdata)
580 exec_func(task, localdata)
581 for func in (postfuncs or '').split():
582 exec_func(func, localdata)
583 except FuncFailed as exc:
584 if quieterr:
585 event.fire(TaskFailedSilent(task, logfn, localdata), localdata)
586 else:
587 errprinted = errchk.triggered
588 logger.error(str(exc))
589 event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata)
590 return 1
591 except bb.BBHandledException:
592 event.fire(TaskFailed(task, logfn, localdata, True), localdata)
593 return 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500594 finally:
595 sys.stdout.flush()
596 sys.stderr.flush()
597
598 bblogger.removeHandler(handler)
599
600 # Restore the backup fds
601 os.dup2(osi[0], osi[1])
602 os.dup2(oso[0], oso[1])
603 os.dup2(ose[0], ose[1])
604
605 # Close the backup fds
606 os.close(osi[0])
607 os.close(oso[0])
608 os.close(ose[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500609
610 logfile.close()
611 if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
612 logger.debug(2, "Zero size logfn %s, removing", logfn)
613 bb.utils.remove(logfn)
614 bb.utils.remove(loglink)
615 event.fire(TaskSucceeded(task, logfn, localdata), localdata)
616
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500617 if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500618 make_stamp(task, localdata)
619
620 return 0
621
622def exec_task(fn, task, d, profile = False):
623 try:
624 quieterr = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500625 if d.getVarFlag(task, "quieterrors", False) is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500626 quieterr = True
627
628 if profile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500629 profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500630 try:
631 import cProfile as profile
632 except:
633 import profile
634 prof = profile.Profile()
635 ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
636 prof.dump_stats(profname)
637 bb.utils.process_profilelog(profname)
638
639 return ret
640 else:
641 return _exec_task(fn, task, d, quieterr)
642
643 except Exception:
644 from traceback import format_exc
645 if not quieterr:
646 logger.error("Build of %s failed" % (task))
647 logger.error(format_exc())
648 failedevent = TaskFailed(task, None, d, True)
649 event.fire(failedevent, d)
650 return 1
651
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600652def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500653 """
654 Internal stamp helper function
655 Makes sure the stamp directory exists
656 Returns the stamp path+filename
657
658 In the bitbake core, d can be a CacheData and file_name will be set.
659 When called in task context, d will be a data store, file_name will not be set
660 """
661 taskflagname = taskname
662 if taskname.endswith("_setscene") and taskname != "do_setscene":
663 taskflagname = taskname.replace("_setscene", "")
664
665 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500666 stamp = d.stamp[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500667 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
668 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500669 stamp = d.getVar('STAMP')
670 file_name = d.getVar('BB_FILENAME')
671 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500672
673 if baseonly:
674 return stamp
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600675 if noextra:
676 extrainfo = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500677
678 if not stamp:
679 return
680
681 stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
682
683 stampdir = os.path.dirname(stamp)
684 if cached_mtime_noerror(stampdir) == 0:
685 bb.utils.mkdirhier(stampdir)
686
687 return stamp
688
689def stamp_cleanmask_internal(taskname, d, file_name):
690 """
691 Internal stamp helper function to generate stamp cleaning mask
692 Returns the stamp path+filename
693
694 In the bitbake core, d can be a CacheData and file_name will be set.
695 When called in task context, d will be a data store, file_name will not be set
696 """
697 taskflagname = taskname
698 if taskname.endswith("_setscene") and taskname != "do_setscene":
699 taskflagname = taskname.replace("_setscene", "")
700
701 if file_name:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500702 stamp = d.stampclean[file_name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500703 extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
704 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500705 stamp = d.getVar('STAMPCLEAN')
706 file_name = d.getVar('BB_FILENAME')
707 extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500708
709 if not stamp:
710 return []
711
712 cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
713
714 return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
715
716def make_stamp(task, d, file_name = None):
717 """
718 Creates/updates a stamp for a given task
719 (d can be a data dict or dataCache)
720 """
721 cleanmask = stamp_cleanmask_internal(task, d, file_name)
722 for mask in cleanmask:
723 for name in glob.glob(mask):
724 # Preserve sigdata files in the stamps directory
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500725 if "sigdata" in name or "sigbasedata" in name:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500726 continue
727 # Preserve taint files in the stamps directory
728 if name.endswith('.taint'):
729 continue
730 os.unlink(name)
731
732 stamp = stamp_internal(task, d, file_name)
733 # Remove the file and recreate to force timestamp
734 # change on broken NFS filesystems
735 if stamp:
736 bb.utils.remove(stamp)
737 open(stamp, "w").close()
738
739 # If we're in task context, write out a signature file for each task
740 # as it completes
741 if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
742 stampbase = stamp_internal(task, d, None, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500743 file_name = d.getVar('BB_FILENAME')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500744 bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
745
746def del_stamp(task, d, file_name = None):
747 """
748 Removes a stamp for a given task
749 (d can be a data dict or dataCache)
750 """
751 stamp = stamp_internal(task, d, file_name)
752 bb.utils.remove(stamp)
753
754def write_taint(task, d, file_name = None):
755 """
756 Creates a "taint" file which will force the specified task and its
757 dependents to be re-run the next time by influencing the value of its
758 taskhash.
759 (d can be a data dict or dataCache)
760 """
761 import uuid
762 if file_name:
763 taintfn = d.stamp[file_name] + '.' + task + '.taint'
764 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500765 taintfn = d.getVar('STAMP') + '.' + task + '.taint'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500766 bb.utils.mkdirhier(os.path.dirname(taintfn))
767 # The specific content of the taint file is not really important,
768 # we just need it to be random, so a random UUID is used
769 with open(taintfn, 'w') as taintf:
770 taintf.write(str(uuid.uuid4()))
771
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600772def stampfile(taskname, d, file_name = None, noextra=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500773 """
774 Return the stamp for a given task
775 (d can be a data dict or dataCache)
776 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600777 return stamp_internal(taskname, d, file_name, noextra=noextra)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500778
779def add_tasks(tasklist, d):
780 task_deps = d.getVar('_task_deps', False)
781 if not task_deps:
782 task_deps = {}
783 if not 'tasks' in task_deps:
784 task_deps['tasks'] = []
785 if not 'parents' in task_deps:
786 task_deps['parents'] = {}
787
788 for task in tasklist:
789 task = d.expand(task)
790
791 d.setVarFlag(task, 'task', 1)
792
793 if not task in task_deps['tasks']:
794 task_deps['tasks'].append(task)
795
796 flags = d.getVarFlags(task)
797 def getTask(name):
798 if not name in task_deps:
799 task_deps[name] = {}
800 if name in flags:
801 deptask = d.expand(flags[name])
802 task_deps[name][task] = deptask
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800803 getTask('mcdepends')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500804 getTask('depends')
805 getTask('rdepends')
806 getTask('deptask')
807 getTask('rdeptask')
808 getTask('recrdeptask')
809 getTask('recideptask')
810 getTask('nostamp')
811 getTask('fakeroot')
812 getTask('noexec')
813 getTask('umask')
814 task_deps['parents'][task] = []
815 if 'deps' in flags:
816 for dep in flags['deps']:
817 dep = d.expand(dep)
818 task_deps['parents'][task].append(dep)
819
820 # don't assume holding a reference
821 d.setVar('_task_deps', task_deps)
822
823def addtask(task, before, after, d):
824 if task[:3] != "do_":
825 task = "do_" + task
826
827 d.setVarFlag(task, "task", 1)
828 bbtasks = d.getVar('__BBTASKS', False) or []
829 if task not in bbtasks:
830 bbtasks.append(task)
831 d.setVar('__BBTASKS', bbtasks)
832
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500833 existing = d.getVarFlag(task, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834 if after is not None:
835 # set up deps for function
836 for entry in after.split():
837 if entry not in existing:
838 existing.append(entry)
839 d.setVarFlag(task, "deps", existing)
840 if before is not None:
841 # set up things that depend on this func
842 for entry in before.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500843 existing = d.getVarFlag(entry, "deps", False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844 if task not in existing:
845 d.setVarFlag(entry, "deps", [task] + existing)
846
847def deltask(task, d):
848 if task[:3] != "do_":
849 task = "do_" + task
850
851 bbtasks = d.getVar('__BBTASKS', False) or []
852 if task in bbtasks:
853 bbtasks.remove(task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600854 d.delVarFlag(task, 'task')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500855 d.setVar('__BBTASKS', bbtasks)
856
857 d.delVarFlag(task, 'deps')
858 for bbtask in d.getVar('__BBTASKS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500859 deps = d.getVarFlag(bbtask, 'deps', False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500860 if task in deps:
861 deps.remove(task)
862 d.setVarFlag(bbtask, 'deps', deps)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500863
864def preceedtask(task, with_recrdeptasks, d):
865 """
866 Returns a set of tasks in the current recipe which were specified as
867 precondition by the task itself ("after") or which listed themselves
868 as precondition ("before"). Preceeding tasks specified via the
869 "recrdeptask" are included in the result only if requested. Beware
870 that this may lead to the task itself being listed.
871 """
872 preceed = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400873
874 # Ignore tasks which don't exist
875 tasks = d.getVar('__BBTASKS', False)
876 if task not in tasks:
877 return preceed
878
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500879 preceed.update(d.getVarFlag(task, 'deps') or [])
880 if with_recrdeptasks:
881 recrdeptask = d.getVarFlag(task, 'recrdeptask')
882 if recrdeptask:
883 preceed.update(recrdeptask.split())
884 return preceed
885
886def tasksbetween(task_start, task_end, d):
887 """
888 Return the list of tasks between two tasks in the current recipe,
889 where task_start is to start at and task_end is the task to end at
890 (and task_end has a dependency chain back to task_start).
891 """
892 outtasks = []
893 tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
894 def follow_chain(task, endtask, chain=None):
895 if not chain:
896 chain = []
897 chain.append(task)
898 for othertask in tasks:
899 if othertask == task:
900 continue
901 if task == endtask:
902 for ctask in chain:
903 if ctask not in outtasks:
904 outtasks.append(ctask)
905 else:
906 deps = d.getVarFlag(othertask, 'deps', False)
907 if task in deps:
908 follow_chain(othertask, endtask, chain)
909 chain.pop()
910 follow_chain(task_start, task_end)
911 return outtasks