blob: ce711b62523837a063eeb3c3d4bf3aa91cdb16a6 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""
2BitBake 'RunQueue' implementation
3
4Handles preparation and execution of a queue of tasks
5"""
6
7# Copyright (C) 2006-2007 Richard Purdie
8#
Brad Bishopc342db32019-05-15 21:57:59 -04009# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011
12import copy
13import os
14import sys
Patrick Williamsc124f4f2015-09-15 14:41:29 -050015import stat
Patrick Williamsc124f4f2015-09-15 14:41:29 -050016import errno
17import logging
18import re
19import bb
Andrew Geissler82c905d2020-04-13 13:39:40 -050020from bb import msg, event
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021from bb import monitordisk
22import subprocess
Patrick Williamsc0f7c042017-02-23 20:41:17 -060023import pickle
Brad Bishop6e60e8b2018-02-01 10:27:11 -050024from multiprocessing import Process
Brad Bishop19323692019-04-05 15:28:33 -040025import shlex
Brad Bishop96ff1982019-08-19 13:50:42 -040026import pprint
Patrick Williamsdb4c27e2022-08-05 08:10:29 -050027import time
Patrick Williamsc124f4f2015-09-15 14:41:29 -050028
29bblogger = logging.getLogger("BitBake")
30logger = logging.getLogger("BitBake.RunQueue")
Andrew Geissler82c905d2020-04-13 13:39:40 -050031hashequiv_logger = logging.getLogger("BitBake.RunQueue.HashEquiv")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032
Brad Bishop19323692019-04-05 15:28:33 -040033__find_sha256__ = re.compile( r'(?i)(?<![a-z0-9])[a-f0-9]{64}(?![a-z0-9])' )
Patrick Williamsc124f4f2015-09-15 14:41:29 -050034
Patrick Williamsc0f7c042017-02-23 20:41:17 -060035def fn_from_tid(tid):
36 return tid.rsplit(":", 1)[0]
37
38def taskname_from_tid(tid):
39 return tid.rsplit(":", 1)[1]
40
Andrew Geissler99467da2019-02-25 18:54:23 -060041def mc_from_tid(tid):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060042 if tid.startswith('mc:') and tid.count(':') >= 2:
Andrew Geissler99467da2019-02-25 18:54:23 -060043 return tid.split(':')[1]
44 return ""
45
Patrick Williamsc0f7c042017-02-23 20:41:17 -060046def split_tid(tid):
47 (mc, fn, taskname, _) = split_tid_mcfn(tid)
48 return (mc, fn, taskname)
49
Andrew Geissler5a43b432020-06-13 10:46:56 -050050def split_mc(n):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060051 if n.startswith("mc:") and n.count(':') >= 2:
Andrew Geissler5a43b432020-06-13 10:46:56 -050052 _, mc, n = n.split(":", 2)
53 return (mc, n)
54 return ('', n)
55
Patrick Williamsc0f7c042017-02-23 20:41:17 -060056def split_tid_mcfn(tid):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060057 if tid.startswith('mc:') and tid.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060058 elems = tid.split(':')
59 mc = elems[1]
60 fn = ":".join(elems[2:-1])
61 taskname = elems[-1]
Brad Bishop15ae2502019-06-18 21:44:24 -040062 mcfn = "mc:" + mc + ":" + fn
Patrick Williamsc0f7c042017-02-23 20:41:17 -060063 else:
64 tid = tid.rsplit(":", 1)
65 mc = ""
66 fn = tid[0]
67 taskname = tid[1]
68 mcfn = fn
69
70 return (mc, fn, taskname, mcfn)
71
72def build_tid(mc, fn, taskname):
73 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -040074 return "mc:" + mc + ":" + fn + ":" + taskname
Patrick Williamsc0f7c042017-02-23 20:41:17 -060075 return fn + ":" + taskname
76
Brad Bishop96ff1982019-08-19 13:50:42 -040077# Index used to pair up potentially matching multiconfig tasks
78# We match on PN, taskname and hash being equal
79def pending_hash_index(tid, rqdata):
80 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
81 pn = rqdata.dataCaches[mc].pkg_fn[taskfn]
Brad Bishop00e122a2019-10-05 11:10:57 -040082 h = rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -040083 return pn + ":" + "taskname" + h
84
Patrick Williamsc124f4f2015-09-15 14:41:29 -050085class RunQueueStats:
86 """
87 Holds statistics on the tasks handled by the associated runQueue
88 """
Andrew Geissler5199d832021-09-24 16:47:35 -050089 def __init__(self, total, setscene_total):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090 self.completed = 0
91 self.skipped = 0
92 self.failed = 0
93 self.active = 0
Andrew Geissler5199d832021-09-24 16:47:35 -050094 self.setscene_active = 0
95 self.setscene_covered = 0
96 self.setscene_notcovered = 0
97 self.setscene_total = setscene_total
Patrick Williamsc124f4f2015-09-15 14:41:29 -050098 self.total = total
99
100 def copy(self):
Andrew Geissler5199d832021-09-24 16:47:35 -0500101 obj = self.__class__(self.total, self.setscene_total)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102 obj.__dict__.update(self.__dict__)
103 return obj
104
105 def taskFailed(self):
106 self.active = self.active - 1
107 self.failed = self.failed + 1
108
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800109 def taskCompleted(self):
110 self.active = self.active - 1
111 self.completed = self.completed + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500112
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800113 def taskSkipped(self):
114 self.active = self.active + 1
115 self.skipped = self.skipped + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500116
117 def taskActive(self):
118 self.active = self.active + 1
119
Andrew Geissler5199d832021-09-24 16:47:35 -0500120 def updateCovered(self, covered, notcovered):
121 self.setscene_covered = covered
122 self.setscene_notcovered = notcovered
123
124 def updateActiveSetscene(self, active):
125 self.setscene_active = active
126
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127# These values indicate the next step due to be run in the
128# runQueue state machine
129runQueuePrepare = 2
130runQueueSceneInit = 3
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131runQueueRunning = 6
132runQueueFailed = 7
133runQueueCleanUp = 8
134runQueueComplete = 9
135
136class RunQueueScheduler(object):
137 """
138 Control the order tasks are scheduled in.
139 """
140 name = "basic"
141
142 def __init__(self, runqueue, rqdata):
143 """
144 The default scheduler just returns the first buildable task (the
145 priority map is sorted by task number)
146 """
147 self.rq = runqueue
148 self.rqdata = rqdata
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600149 self.numTasks = len(self.rqdata.runtaskentries)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600151 self.prio_map = [self.rqdata.runtaskentries.keys()]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500152
Brad Bishop08902b02019-08-20 09:16:51 -0400153 self.buildable = set()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800154 self.skip_maxthread = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500155 self.stamps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600156 for tid in self.rqdata.runtaskentries:
157 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -0600158 self.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600159 if tid in self.rq.runq_buildable:
160 self.buildable.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500161
162 self.rev_prio_map = None
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500163 self.is_pressure_usable()
164
165 def is_pressure_usable(self):
166 """
167 If monitoring pressure, return True if pressure files can be open and read. For example
168 openSUSE /proc/pressure/* files have readable file permissions but when read the error EOPNOTSUPP (Operation not supported)
169 is returned.
170 """
Patrick Williams92b42cb2022-09-03 06:53:57 -0500171 if self.rq.max_cpu_pressure or self.rq.max_io_pressure or self.rq.max_memory_pressure:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500172 try:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500173 with open("/proc/pressure/cpu") as cpu_pressure_fds, \
174 open("/proc/pressure/io") as io_pressure_fds, \
175 open("/proc/pressure/memory") as memory_pressure_fds:
176
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500177 self.prev_cpu_pressure = cpu_pressure_fds.readline().split()[4].split("=")[1]
178 self.prev_io_pressure = io_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williams92b42cb2022-09-03 06:53:57 -0500179 self.prev_memory_pressure = memory_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500180 self.prev_pressure_time = time.time()
181 self.check_pressure = True
182 except:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500183 bb.note("The /proc/pressure files can't be read. Continuing build without monitoring pressure")
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500184 self.check_pressure = False
185 else:
186 self.check_pressure = False
187
188 def exceeds_max_pressure(self):
189 """
190 Monitor the difference in total pressure at least once per second, if
Patrick Williams92b42cb2022-09-03 06:53:57 -0500191 BB_PRESSURE_MAX_{CPU|IO|MEMORY} are set, return True if above threshold.
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500192 """
193 if self.check_pressure:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500194 with open("/proc/pressure/cpu") as cpu_pressure_fds, \
195 open("/proc/pressure/io") as io_pressure_fds, \
196 open("/proc/pressure/memory") as memory_pressure_fds:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500197 # extract "total" from /proc/pressure/{cpu|io}
198 curr_cpu_pressure = cpu_pressure_fds.readline().split()[4].split("=")[1]
199 curr_io_pressure = io_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williams92b42cb2022-09-03 06:53:57 -0500200 curr_memory_pressure = memory_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500201 exceeds_cpu_pressure = self.rq.max_cpu_pressure and (float(curr_cpu_pressure) - float(self.prev_cpu_pressure)) > self.rq.max_cpu_pressure
202 exceeds_io_pressure = self.rq.max_io_pressure and (float(curr_io_pressure) - float(self.prev_io_pressure)) > self.rq.max_io_pressure
Patrick Williams92b42cb2022-09-03 06:53:57 -0500203 exceeds_memory_pressure = self.rq.max_memory_pressure and (float(curr_memory_pressure) - float(self.prev_memory_pressure)) > self.rq.max_memory_pressure
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500204 now = time.time()
205 if now - self.prev_pressure_time > 1.0:
206 self.prev_cpu_pressure = curr_cpu_pressure
207 self.prev_io_pressure = curr_io_pressure
Patrick Williams92b42cb2022-09-03 06:53:57 -0500208 self.prev_memory_pressure = curr_memory_pressure
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500209 self.prev_pressure_time = now
Patrick Williams92b42cb2022-09-03 06:53:57 -0500210 return (exceeds_cpu_pressure or exceeds_io_pressure or exceeds_memory_pressure)
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500211 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500212
213 def next_buildable_task(self):
214 """
215 Return the id of the first task we find that is buildable
216 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500217 # Once tasks are running we don't need to worry about them again
218 self.buildable.difference_update(self.rq.runq_running)
Brad Bishop08902b02019-08-20 09:16:51 -0400219 buildable = set(self.buildable)
Brad Bishop08902b02019-08-20 09:16:51 -0400220 buildable.difference_update(self.rq.holdoff_tasks)
221 buildable.intersection_update(self.rq.tasks_covered | self.rq.tasks_notcovered)
Brad Bishop96ff1982019-08-19 13:50:42 -0400222 if not buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500223 return None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800224
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500225 # Bitbake requires that at least one task be active. Only check for pressure if
226 # this is the case, otherwise the pressure limitation could result in no tasks
227 # being active and no new tasks started thereby, at times, breaking the scheduler.
228 if self.rq.stats.active and self.exceeds_max_pressure():
229 return None
230
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800231 # Filter out tasks that have a max number of threads that have been exceeded
232 skip_buildable = {}
233 for running in self.rq.runq_running.difference(self.rq.runq_complete):
234 rtaskname = taskname_from_tid(running)
235 if rtaskname not in self.skip_maxthread:
236 self.skip_maxthread[rtaskname] = self.rq.cfgData.getVarFlag(rtaskname, "number_threads")
237 if not self.skip_maxthread[rtaskname]:
238 continue
239 if rtaskname in skip_buildable:
240 skip_buildable[rtaskname] += 1
241 else:
242 skip_buildable[rtaskname] = 1
243
Brad Bishop96ff1982019-08-19 13:50:42 -0400244 if len(buildable) == 1:
Brad Bishop08902b02019-08-20 09:16:51 -0400245 tid = buildable.pop()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800246 taskname = taskname_from_tid(tid)
247 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
248 return None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600249 stamp = self.stamps[tid]
250 if stamp not in self.rq.build_stamps.values():
251 return tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500252
253 if not self.rev_prio_map:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600254 self.rev_prio_map = {}
255 for tid in self.rqdata.runtaskentries:
256 self.rev_prio_map[tid] = self.prio_map.index(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500257
258 best = None
259 bestprio = None
Brad Bishop96ff1982019-08-19 13:50:42 -0400260 for tid in buildable:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800261 taskname = taskname_from_tid(tid)
262 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
263 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600264 prio = self.rev_prio_map[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500265 if bestprio is None or bestprio > prio:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600266 stamp = self.stamps[tid]
267 if stamp in self.rq.build_stamps.values():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500268 continue
269 bestprio = prio
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600270 best = tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271
272 return best
273
274 def next(self):
275 """
276 Return the id of the task we should build next
277 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800278 if self.rq.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500279 return self.next_buildable_task()
280
Brad Bishop316dfdd2018-06-25 12:45:53 -0400281 def newbuildable(self, task):
Brad Bishop08902b02019-08-20 09:16:51 -0400282 self.buildable.add(task)
283
284 def removebuildable(self, task):
285 self.buildable.remove(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500286
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500287 def describe_task(self, taskid):
288 result = 'ID %s' % taskid
289 if self.rev_prio_map:
290 result = result + (' pri %d' % self.rev_prio_map[taskid])
291 return result
292
293 def dump_prio(self, comment):
294 bb.debug(3, '%s (most important first):\n%s' %
295 (comment,
296 '\n'.join(['%d. %s' % (index + 1, self.describe_task(taskid)) for
297 index, taskid in enumerate(self.prio_map)])))
298
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500299class RunQueueSchedulerSpeed(RunQueueScheduler):
300 """
301 A scheduler optimised for speed. The priority map is sorted by task weight,
302 heavier weighted tasks (tasks needed by the most other tasks) are run first.
303 """
304 name = "speed"
305
306 def __init__(self, runqueue, rqdata):
307 """
308 The priority map is sorted by task weight.
309 """
310 RunQueueScheduler.__init__(self, runqueue, rqdata)
311
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600312 weights = {}
313 for tid in self.rqdata.runtaskentries:
314 weight = self.rqdata.runtaskentries[tid].weight
315 if not weight in weights:
316 weights[weight] = []
317 weights[weight].append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500318
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600319 self.prio_map = []
320 for weight in sorted(weights):
321 for w in weights[weight]:
322 self.prio_map.append(w)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500323
324 self.prio_map.reverse()
325
326class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed):
327 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500328 A scheduler optimised to complete .bb files as quickly as possible. The
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500329 priority map is sorted by task weight, but then reordered so once a given
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500330 .bb file starts to build, it's completed as quickly as possible by
331 running all tasks related to the same .bb file one after the after.
332 This works well where disk space is at a premium and classes like OE's
333 rm_work are in force.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500334 """
335 name = "completion"
336
337 def __init__(self, runqueue, rqdata):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500338 super(RunQueueSchedulerCompletion, self).__init__(runqueue, rqdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500339
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500340 # Extract list of tasks for each recipe, with tasks sorted
341 # ascending from "must run first" (typically do_fetch) to
342 # "runs last" (do_build). The speed scheduler prioritizes
343 # tasks that must run first before the ones that run later;
344 # this is what we depend on here.
345 task_lists = {}
346 for taskid in self.prio_map:
347 fn, taskname = taskid.rsplit(':', 1)
348 task_lists.setdefault(fn, []).append(taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500349
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500350 # Now unify the different task lists. The strategy is that
351 # common tasks get skipped and new ones get inserted after the
352 # preceeding common one(s) as they are found. Because task
353 # lists should differ only by their number of tasks, but not
354 # the ordering of the common tasks, this should result in a
355 # deterministic result that is a superset of the individual
356 # task ordering.
357 all_tasks = []
358 for recipe, new_tasks in task_lists.items():
359 index = 0
360 old_task = all_tasks[index] if index < len(all_tasks) else None
361 for new_task in new_tasks:
362 if old_task == new_task:
363 # Common task, skip it. This is the fast-path which
364 # avoids a full search.
365 index += 1
366 old_task = all_tasks[index] if index < len(all_tasks) else None
367 else:
368 try:
369 index = all_tasks.index(new_task)
370 # Already present, just not at the current
371 # place. We re-synchronized by changing the
372 # index so that it matches again. Now
373 # move on to the next existing task.
374 index += 1
375 old_task = all_tasks[index] if index < len(all_tasks) else None
376 except ValueError:
377 # Not present. Insert before old_task, which
378 # remains the same (but gets shifted back).
379 all_tasks.insert(index, new_task)
380 index += 1
381 bb.debug(3, 'merged task list: %s' % all_tasks)
382
383 # Now reverse the order so that tasks that finish the work on one
384 # recipe are considered more imporant (= come first). The ordering
385 # is now so that do_build is most important.
386 all_tasks.reverse()
387
388 # Group tasks of the same kind before tasks of less important
389 # kinds at the head of the queue (because earlier = lower
390 # priority number = runs earlier), while preserving the
391 # ordering by recipe. If recipe foo is more important than
392 # bar, then the goal is to work on foo's do_populate_sysroot
393 # before bar's do_populate_sysroot and on the more important
394 # tasks of foo before any of the less important tasks in any
395 # other recipe (if those other recipes are more important than
396 # foo).
397 #
398 # All of this only applies when tasks are runable. Explicit
399 # dependencies still override this ordering by priority.
400 #
401 # Here's an example why this priority re-ordering helps with
402 # minimizing disk usage. Consider a recipe foo with a higher
403 # priority than bar where foo DEPENDS on bar. Then the
404 # implicit rule (from base.bbclass) is that foo's do_configure
405 # depends on bar's do_populate_sysroot. This ensures that
406 # bar's do_populate_sysroot gets done first. Normally the
407 # tasks from foo would continue to run once that is done, and
408 # bar only gets completed and cleaned up later. By ordering
409 # bar's task that depend on bar's do_populate_sysroot before foo's
410 # do_configure, that problem gets avoided.
411 task_index = 0
412 self.dump_prio('original priorities')
413 for task in all_tasks:
414 for index in range(task_index, self.numTasks):
415 taskid = self.prio_map[index]
416 taskname = taskid.rsplit(':', 1)[1]
417 if taskname == task:
418 del self.prio_map[index]
419 self.prio_map.insert(task_index, taskid)
420 task_index += 1
421 self.dump_prio('completion priorities')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500422
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600423class RunTaskEntry(object):
424 def __init__(self):
425 self.depends = set()
426 self.revdeps = set()
427 self.hash = None
Brad Bishop19323692019-04-05 15:28:33 -0400428 self.unihash = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600429 self.task = None
430 self.weight = 1
431
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500432class RunQueueData:
433 """
434 BitBake Run Queue implementation
435 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600436 def __init__(self, rq, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500437 self.cooker = cooker
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600438 self.dataCaches = dataCaches
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500439 self.taskData = taskData
440 self.targets = targets
441 self.rq = rq
442 self.warn_multi_bb = False
443
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000444 self.multi_provider_allowed = (cfgData.getVar("BB_MULTI_PROVIDER_ALLOWED") or "").split()
445 self.setscene_ignore_tasks = get_setscene_enforce_ignore_tasks(cfgData, targets)
446 self.setscene_ignore_tasks_checked = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500447 self.setscene_enforce = (cfgData.getVar('BB_SETSCENE_ENFORCE') == "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600448 self.init_progress_reporter = bb.progress.DummyMultiStageProcessProgressReporter()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500449
450 self.reset()
451
452 def reset(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600453 self.runtaskentries = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500454
455 def runq_depends_names(self, ids):
456 import re
457 ret = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600458 for id in ids:
459 nam = os.path.basename(id)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500460 nam = re.sub("_[^,]*,", ",", nam)
461 ret.extend([nam])
462 return ret
463
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600464 def get_task_hash(self, tid):
465 return self.runtaskentries[tid].hash
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500466
Brad Bishop19323692019-04-05 15:28:33 -0400467 def get_task_unihash(self, tid):
468 return self.runtaskentries[tid].unihash
469
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600470 def get_user_idstring(self, tid, task_name_suffix = ""):
471 return tid + task_name_suffix
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500472
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500473 def get_short_user_idstring(self, task, task_name_suffix = ""):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500474 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
475 pn = self.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600476 taskname = taskname_from_tid(task) + task_name_suffix
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500477 return "%s:%s" % (pn, taskname)
478
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500479 def circular_depchains_handler(self, tasks):
480 """
481 Some tasks aren't buildable, likely due to circular dependency issues.
482 Identify the circular dependencies and print them in a user readable format.
483 """
484 from copy import deepcopy
485
486 valid_chains = []
487 explored_deps = {}
488 msgs = []
489
Andrew Geissler99467da2019-02-25 18:54:23 -0600490 class TooManyLoops(Exception):
491 pass
492
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500493 def chain_reorder(chain):
494 """
495 Reorder a dependency chain so the lowest task id is first
496 """
497 lowest = 0
498 new_chain = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600499 for entry in range(len(chain)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500500 if chain[entry] < chain[lowest]:
501 lowest = entry
502 new_chain.extend(chain[lowest:])
503 new_chain.extend(chain[:lowest])
504 return new_chain
505
506 def chain_compare_equal(chain1, chain2):
507 """
508 Compare two dependency chains and see if they're the same
509 """
510 if len(chain1) != len(chain2):
511 return False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600512 for index in range(len(chain1)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500513 if chain1[index] != chain2[index]:
514 return False
515 return True
516
517 def chain_array_contains(chain, chain_array):
518 """
519 Return True if chain_array contains chain
520 """
521 for ch in chain_array:
522 if chain_compare_equal(ch, chain):
523 return True
524 return False
525
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600526 def find_chains(tid, prev_chain):
527 prev_chain.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500528 total_deps = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600529 total_deps.extend(self.runtaskentries[tid].revdeps)
530 for revdep in self.runtaskentries[tid].revdeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500531 if revdep in prev_chain:
532 idx = prev_chain.index(revdep)
533 # To prevent duplicates, reorder the chain to start with the lowest taskid
534 # and search through an array of those we've already printed
535 chain = prev_chain[idx:]
536 new_chain = chain_reorder(chain)
537 if not chain_array_contains(new_chain, valid_chains):
538 valid_chains.append(new_chain)
539 msgs.append("Dependency loop #%d found:\n" % len(valid_chains))
540 for dep in new_chain:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600541 msgs.append(" Task %s (dependent Tasks %s)\n" % (dep, self.runq_depends_names(self.runtaskentries[dep].depends)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500542 msgs.append("\n")
543 if len(valid_chains) > 10:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000544 msgs.append("Halted dependency loops search after 10 matches.\n")
Andrew Geissler99467da2019-02-25 18:54:23 -0600545 raise TooManyLoops
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500546 continue
547 scan = False
548 if revdep not in explored_deps:
549 scan = True
550 elif revdep in explored_deps[revdep]:
551 scan = True
552 else:
553 for dep in prev_chain:
554 if dep in explored_deps[revdep]:
555 scan = True
556 if scan:
557 find_chains(revdep, copy.deepcopy(prev_chain))
558 for dep in explored_deps[revdep]:
559 if dep not in total_deps:
560 total_deps.append(dep)
561
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600562 explored_deps[tid] = total_deps
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500563
Andrew Geissler99467da2019-02-25 18:54:23 -0600564 try:
565 for task in tasks:
566 find_chains(task, [])
567 except TooManyLoops:
568 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500569
570 return msgs
571
572 def calculate_task_weights(self, endpoints):
573 """
574 Calculate a number representing the "weight" of each task. Heavier weighted tasks
575 have more dependencies and hence should be executed sooner for maximum speed.
576
577 This function also sanity checks the task list finding tasks that are not
578 possible to execute due to circular dependencies.
579 """
580
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600581 numTasks = len(self.runtaskentries)
582 weight = {}
583 deps_left = {}
584 task_done = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500585
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600586 for tid in self.runtaskentries:
587 task_done[tid] = False
588 weight[tid] = 1
589 deps_left[tid] = len(self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500590
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600591 for tid in endpoints:
592 weight[tid] = 10
593 task_done[tid] = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500594
595 while True:
596 next_points = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600597 for tid in endpoints:
598 for revdep in self.runtaskentries[tid].depends:
599 weight[revdep] = weight[revdep] + weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500600 deps_left[revdep] = deps_left[revdep] - 1
601 if deps_left[revdep] == 0:
602 next_points.append(revdep)
603 task_done[revdep] = True
604 endpoints = next_points
Andrew Geissler595f6302022-01-24 19:11:47 +0000605 if not next_points:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500606 break
607
608 # Circular dependency sanity check
609 problem_tasks = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600610 for tid in self.runtaskentries:
611 if task_done[tid] is False or deps_left[tid] != 0:
612 problem_tasks.append(tid)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600613 logger.debug2("Task %s is not buildable", tid)
614 logger.debug2("(Complete marker was %s and the remaining dependency count was %s)\n", task_done[tid], deps_left[tid])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600615 self.runtaskentries[tid].weight = weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616
617 if problem_tasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600618 message = "%s unbuildable tasks were found.\n" % len(problem_tasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500619 message = message + "These are usually caused by circular dependencies and any circular dependency chains found will be printed below. Increase the debug level to see a list of unbuildable tasks.\n\n"
620 message = message + "Identifying dependency loops (this may take a short while)...\n"
621 logger.error(message)
622
623 msgs = self.circular_depchains_handler(problem_tasks)
624
625 message = "\n"
626 for msg in msgs:
627 message = message + msg
628 bb.msg.fatal("RunQueue", message)
629
630 return weight
631
632 def prepare(self):
633 """
634 Turn a set of taskData into a RunQueue and compute data needed
635 to optimise the execution order.
636 """
637
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600638 runq_build = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500639 recursivetasks = {}
640 recursiveitasks = {}
641 recursivetasksselfref = set()
642
643 taskData = self.taskData
644
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600645 found = False
646 for mc in self.taskData:
Andrew Geissler595f6302022-01-24 19:11:47 +0000647 if taskData[mc].taskentries:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600648 found = True
649 break
650 if not found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 # Nothing to do
652 return 0
653
Andrew Geissler517393d2023-01-13 08:55:19 -0600654 bb.parse.siggen.setup_datacache(self.dataCaches)
655
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600656 self.init_progress_reporter.start()
657 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500658
659 # Step A - Work out a list of tasks to run
660 #
661 # Taskdata gives us a list of possible providers for every build and run
662 # target ordered by priority. It also gives information on each of those
663 # providers.
664 #
665 # To create the actual list of tasks to execute we fix the list of
666 # providers and then resolve the dependencies into task IDs. This
667 # process is repeated for each type of dependency (tdepends, deptask,
668 # rdeptast, recrdeptask, idepends).
669
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600670 def add_build_dependencies(depids, tasknames, depends, mc):
671 for depname in depids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500672 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600673 if depname not in taskData[mc].build_targets or not taskData[mc].build_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500674 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600675 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500676 if depdata is None:
677 continue
678 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600679 t = depdata + ":" + taskname
680 if t in taskData[mc].taskentries:
681 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500682
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600683 def add_runtime_dependencies(depids, tasknames, depends, mc):
684 for depname in depids:
685 if depname not in taskData[mc].run_targets or not taskData[mc].run_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500686 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600687 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500688 if depdata is None:
689 continue
690 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600691 t = depdata + ":" + taskname
692 if t in taskData[mc].taskentries:
693 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500694
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800695 def add_mc_dependencies(mc, tid):
696 mcdeps = taskData[mc].get_mcdepends()
697 for dep in mcdeps:
698 mcdependency = dep.split(':')
699 pn = mcdependency[3]
700 frommc = mcdependency[1]
701 mcdep = mcdependency[2]
702 deptask = mcdependency[4]
Andrew Geissler517393d2023-01-13 08:55:19 -0600703 if mcdep not in taskData:
704 bb.fatal("Multiconfig '%s' is referenced in multiconfig dependency '%s' but not enabled in BBMULTICONFIG?" % (mcdep, dep))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800705 if mc == frommc:
706 fn = taskData[mcdep].build_targets[pn][0]
707 newdep = '%s:%s' % (fn,deptask)
708 taskData[mc].taskentries[tid].tdepends.append(newdep)
709
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600710 for mc in taskData:
711 for tid in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500712
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600713 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
714 #runtid = build_tid(mc, fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500715
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600716 #logger.debug2("Processing %s,%s:%s", mc, fn, taskname)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600717
718 depends = set()
719 task_deps = self.dataCaches[mc].task_deps[taskfn]
720
721 self.runtaskentries[tid] = RunTaskEntry()
722
723 if fn in taskData[mc].failed_fns:
724 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800726 # We add multiconfig dependencies before processing internal task deps (tdepends)
727 if 'mcdepends' in task_deps and taskname in task_deps['mcdepends']:
728 add_mc_dependencies(mc, tid)
729
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500730 # Resolve task internal dependencies
731 #
732 # e.g. addtask before X after Y
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600733 for t in taskData[mc].taskentries[tid].tdepends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800734 (depmc, depfn, deptaskname, _) = split_tid_mcfn(t)
735 depends.add(build_tid(depmc, depfn, deptaskname))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736
737 # Resolve 'deptask' dependencies
738 #
739 # e.g. do_sometask[deptask] = "do_someothertask"
740 # (makes sure sometask runs after someothertask of all DEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600741 if 'deptask' in task_deps and taskname in task_deps['deptask']:
742 tasknames = task_deps['deptask'][taskname].split()
743 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500744
745 # Resolve 'rdeptask' dependencies
746 #
747 # e.g. do_sometask[rdeptask] = "do_someothertask"
748 # (makes sure sometask runs after someothertask of all RDEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600749 if 'rdeptask' in task_deps and taskname in task_deps['rdeptask']:
750 tasknames = task_deps['rdeptask'][taskname].split()
751 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500752
753 # Resolve inter-task dependencies
754 #
755 # e.g. do_sometask[depends] = "targetname:do_someothertask"
756 # (makes sure sometask runs after targetname's someothertask)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600757 idepends = taskData[mc].taskentries[tid].idepends
758 for (depname, idependtask) in idepends:
759 if depname in taskData[mc].build_targets and taskData[mc].build_targets[depname] and not depname in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500760 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600761 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500762 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600763 t = depdata + ":" + idependtask
764 depends.add(t)
765 if t not in taskData[mc].taskentries:
766 bb.msg.fatal("RunQueue", "Task %s in %s depends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
767 irdepends = taskData[mc].taskentries[tid].irdepends
768 for (depname, idependtask) in irdepends:
769 if depname in taskData[mc].run_targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500770 # Won't be in run_targets if ASSUME_PROVIDED
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500771 if not taskData[mc].run_targets[depname]:
772 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600773 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600775 t = depdata + ":" + idependtask
776 depends.add(t)
777 if t not in taskData[mc].taskentries:
778 bb.msg.fatal("RunQueue", "Task %s in %s rdepends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779
780 # Resolve recursive 'recrdeptask' dependencies (Part A)
781 #
782 # e.g. do_sometask[recrdeptask] = "do_someothertask"
783 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
784 # We cover the recursive part of the dependencies below
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600785 if 'recrdeptask' in task_deps and taskname in task_deps['recrdeptask']:
786 tasknames = task_deps['recrdeptask'][taskname].split()
787 recursivetasks[tid] = tasknames
788 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
789 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
790 if taskname in tasknames:
791 recursivetasksselfref.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500792
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600793 if 'recideptask' in task_deps and taskname in task_deps['recideptask']:
794 recursiveitasks[tid] = []
795 for t in task_deps['recideptask'][taskname].split():
796 newdep = build_tid(mc, fn, t)
797 recursiveitasks[tid].append(newdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500798
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600799 self.runtaskentries[tid].depends = depends
Brad Bishop316dfdd2018-06-25 12:45:53 -0400800 # Remove all self references
801 self.runtaskentries[tid].depends.discard(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500802
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600803 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500804
Brad Bishop316dfdd2018-06-25 12:45:53 -0400805 self.init_progress_reporter.next_stage()
806
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500807 # Resolve recursive 'recrdeptask' dependencies (Part B)
808 #
809 # e.g. do_sometask[recrdeptask] = "do_someothertask"
810 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600811 # We need to do this separately since we need all of runtaskentries[*].depends to be complete before this is processed
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600812
Brad Bishop316dfdd2018-06-25 12:45:53 -0400813 # Generating/interating recursive lists of dependencies is painful and potentially slow
814 # Precompute recursive task dependencies here by:
815 # a) create a temp list of reverse dependencies (revdeps)
816 # b) walk up the ends of the chains (when a given task no longer has dependencies i.e. len(deps) == 0)
817 # c) combine the total list of dependencies in cumulativedeps
818 # d) optimise by pre-truncating 'task' off the items in cumulativedeps (keeps items in sets lower)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500819
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500820
Brad Bishop316dfdd2018-06-25 12:45:53 -0400821 revdeps = {}
822 deps = {}
823 cumulativedeps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600824 for tid in self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400825 deps[tid] = set(self.runtaskentries[tid].depends)
826 revdeps[tid] = set()
827 cumulativedeps[tid] = set()
828 # Generate a temp list of reverse dependencies
829 for tid in self.runtaskentries:
830 for dep in self.runtaskentries[tid].depends:
831 revdeps[dep].add(tid)
832 # Find the dependency chain endpoints
833 endpoints = set()
834 for tid in self.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +0000835 if not deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400836 endpoints.add(tid)
837 # Iterate the chains collating dependencies
838 while endpoints:
839 next = set()
840 for tid in endpoints:
841 for dep in revdeps[tid]:
842 cumulativedeps[dep].add(fn_from_tid(tid))
843 cumulativedeps[dep].update(cumulativedeps[tid])
844 if tid in deps[dep]:
845 deps[dep].remove(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +0000846 if not deps[dep]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400847 next.add(dep)
848 endpoints = next
849 #for tid in deps:
Andrew Geissler595f6302022-01-24 19:11:47 +0000850 # if deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400851 # bb.warn("Sanity test failure, dependencies left for %s (%s)" % (tid, deps[tid]))
852
853 # Loop here since recrdeptasks can depend upon other recrdeptasks and we have to
854 # resolve these recursively until we aren't adding any further extra dependencies
855 extradeps = True
856 while extradeps:
857 extradeps = 0
858 for tid in recursivetasks:
859 tasknames = recursivetasks[tid]
860
861 totaldeps = set(self.runtaskentries[tid].depends)
862 if tid in recursiveitasks:
863 totaldeps.update(recursiveitasks[tid])
864 for dep in recursiveitasks[tid]:
865 if dep not in self.runtaskentries:
866 continue
867 totaldeps.update(self.runtaskentries[dep].depends)
868
869 deps = set()
870 for dep in totaldeps:
871 if dep in cumulativedeps:
872 deps.update(cumulativedeps[dep])
873
874 for t in deps:
875 for taskname in tasknames:
876 newtid = t + ":" + taskname
877 if newtid == tid:
878 continue
879 if newtid in self.runtaskentries and newtid not in self.runtaskentries[tid].depends:
880 extradeps += 1
881 self.runtaskentries[tid].depends.add(newtid)
882
883 # Handle recursive tasks which depend upon other recursive tasks
884 deps = set()
885 for dep in self.runtaskentries[tid].depends.intersection(recursivetasks):
886 deps.update(self.runtaskentries[dep].depends.difference(self.runtaskentries[tid].depends))
887 for newtid in deps:
888 for taskname in tasknames:
889 if not newtid.endswith(":" + taskname):
890 continue
891 if newtid in self.runtaskentries:
892 extradeps += 1
893 self.runtaskentries[tid].depends.add(newtid)
894
895 bb.debug(1, "Added %s recursive dependencies in this loop" % extradeps)
896
897 # Remove recrdeptask circular references so that do_a[recrdeptask] = "do_a do_b" can work
898 for tid in recursivetasksselfref:
899 self.runtaskentries[tid].depends.difference_update(recursivetasksselfref)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600900
901 self.init_progress_reporter.next_stage()
902
903 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500904
905 # Step B - Mark all active tasks
906 #
907 # Start with the tasks we were asked to run and mark all dependencies
908 # as active too. If the task is to be 'forced', clear its stamp. Once
909 # all active tasks are marked, prune the ones we don't need.
910
911 logger.verbose("Marking Active Tasks")
912
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600913 def mark_active(tid, depth):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500914 """
915 Mark an item as active along with its depends
916 (calls itself recursively)
917 """
918
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600919 if tid in runq_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500920 return
921
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600922 runq_build[tid] = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500923
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600924 depends = self.runtaskentries[tid].depends
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500925 for depend in depends:
926 mark_active(depend, depth+1)
927
Brad Bishop79641f22019-09-10 07:20:22 -0400928 def invalidate_task(tid, error_nostamp):
929 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
930 taskdep = self.dataCaches[mc].task_deps[taskfn]
931 if fn + ":" + taskname not in taskData[mc].taskentries:
932 logger.warning("Task %s does not exist, invalidating this task will have no effect" % taskname)
933 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
934 if error_nostamp:
935 bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
936 else:
937 bb.debug(1, "Task %s is marked nostamp, cannot invalidate this task" % taskname)
938 else:
939 logger.verbose("Invalidate task %s, %s", taskname, fn)
Andrew Geissler517393d2023-01-13 08:55:19 -0600940 bb.parse.siggen.invalidate_task(taskname, taskfn)
Brad Bishop79641f22019-09-10 07:20:22 -0400941
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600942 self.target_tids = []
943 for (mc, target, task, fn) in self.targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500944
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600945 if target not in taskData[mc].build_targets or not taskData[mc].build_targets[target]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500946 continue
947
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600948 if target in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500949 continue
950
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500951 parents = False
952 if task.endswith('-'):
953 parents = True
954 task = task[:-1]
955
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600956 if fn in taskData[mc].failed_fns:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500957 continue
958
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600959 # fn already has mc prefix
960 tid = fn + ":" + task
961 self.target_tids.append(tid)
962 if tid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500963 import difflib
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600964 tasks = []
965 for x in taskData[mc].taskentries:
966 if x.startswith(fn + ":"):
967 tasks.append(taskname_from_tid(x))
968 close_matches = difflib.get_close_matches(task, tasks, cutoff=0.7)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500969 if close_matches:
970 extra = ". Close matches:\n %s" % "\n ".join(close_matches)
971 else:
972 extra = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600973 bb.msg.fatal("RunQueue", "Task %s does not exist for target %s (%s)%s" % (task, target, tid, extra))
974
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500975 # For tasks called "XXXX-", ony run their dependencies
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500976 if parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600977 for i in self.runtaskentries[tid].depends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500978 mark_active(i, 1)
979 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600980 mark_active(tid, 1)
981
982 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500983
984 # Step C - Prune all inactive tasks
985 #
986 # Once all active tasks are marked, prune the ones we don't need.
987
Brad Bishop316dfdd2018-06-25 12:45:53 -0400988 # Handle --runall
989 if self.cooker.configuration.runall:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500990 # re-run the mark_active and then drop unused tasks from new list
Andrew Geissler595f6302022-01-24 19:11:47 +0000991 reduced_tasklist = set(self.runtaskentries.keys())
992 for tid in list(self.runtaskentries.keys()):
993 if tid not in runq_build:
994 reduced_tasklist.remove(tid)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500995 runq_build = {}
Brad Bishop316dfdd2018-06-25 12:45:53 -0400996
997 for task in self.cooker.configuration.runall:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500998 if not task.startswith("do_"):
999 task = "do_{0}".format(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001000 runall_tids = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00001001 for tid in reduced_tasklist:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001002 wanttid = "{0}:{1}".format(fn_from_tid(tid), task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001003 if wanttid in self.runtaskentries:
1004 runall_tids.add(wanttid)
1005
1006 for tid in list(runall_tids):
Andrew Geissler595f6302022-01-24 19:11:47 +00001007 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001008 if self.cooker.configuration.force:
1009 invalidate_task(tid, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001010
Andrew Geissler595f6302022-01-24 19:11:47 +00001011 delcount = set()
1012 for tid in list(self.runtaskentries.keys()):
1013 if tid not in runq_build:
1014 delcount.add(tid)
1015 del self.runtaskentries[tid]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001016
Andrew Geissler595f6302022-01-24 19:11:47 +00001017 if self.cooker.configuration.runall:
1018 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001019 bb.msg.fatal("RunQueue", "Could not find any tasks with the tasknames %s to run within the recipes of the taskgraphs of the targets %s" % (str(self.cooker.configuration.runall), str(self.targets)))
1020
1021 self.init_progress_reporter.next_stage()
1022
1023 # Handle runonly
1024 if self.cooker.configuration.runonly:
1025 # re-run the mark_active and then drop unused tasks from new list
1026 runq_build = {}
1027
1028 for task in self.cooker.configuration.runonly:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001029 if not task.startswith("do_"):
1030 task = "do_{0}".format(task)
Andrew Geissler595f6302022-01-24 19:11:47 +00001031 runonly_tids = [k for k in self.runtaskentries.keys() if taskname_from_tid(k) == task]
Brad Bishop316dfdd2018-06-25 12:45:53 -04001032
Andrew Geissler595f6302022-01-24 19:11:47 +00001033 for tid in runonly_tids:
1034 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001035 if self.cooker.configuration.force:
1036 invalidate_task(tid, False)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001037
1038 for tid in list(self.runtaskentries.keys()):
1039 if tid not in runq_build:
Andrew Geissler595f6302022-01-24 19:11:47 +00001040 delcount.add(tid)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001041 del self.runtaskentries[tid]
1042
Andrew Geissler595f6302022-01-24 19:11:47 +00001043 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001044 bb.msg.fatal("RunQueue", "Could not find any tasks with the tasknames %s to run within the taskgraphs of the targets %s" % (str(self.cooker.configuration.runonly), str(self.targets)))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001045
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001046 #
1047 # Step D - Sanity checks and computation
1048 #
1049
1050 # Check to make sure we still have tasks to run
Andrew Geissler595f6302022-01-24 19:11:47 +00001051 if not self.runtaskentries:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001052 if not taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001053 bb.msg.fatal("RunQueue", "All buildable tasks have been run but the build is incomplete (--continue mode). Errors for the tasks that failed will have been printed above.")
1054 else:
1055 bb.msg.fatal("RunQueue", "No active tasks and not in --continue mode?! Please report this bug.")
1056
Brad Bishop316dfdd2018-06-25 12:45:53 -04001057 logger.verbose("Pruned %s inactive tasks, %s left", len(delcount), len(self.runtaskentries))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001058
1059 logger.verbose("Assign Weightings")
1060
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001061 self.init_progress_reporter.next_stage()
1062
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001063 # Generate a list of reverse dependencies to ease future calculations
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001064 for tid in self.runtaskentries:
1065 for dep in self.runtaskentries[tid].depends:
1066 self.runtaskentries[dep].revdeps.add(tid)
1067
1068 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001069
1070 # Identify tasks at the end of dependency chains
1071 # Error on circular dependency loops (length two)
1072 endpoints = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001073 for tid in self.runtaskentries:
1074 revdeps = self.runtaskentries[tid].revdeps
Andrew Geissler595f6302022-01-24 19:11:47 +00001075 if not revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001076 endpoints.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001077 for dep in revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001078 if dep in self.runtaskentries[tid].depends:
1079 bb.msg.fatal("RunQueue", "Task %s has circular dependency on %s" % (tid, dep))
1080
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001081
1082 logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
1083
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001084 self.init_progress_reporter.next_stage()
1085
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001086 # Calculate task weights
1087 # Check of higher length circular dependencies
1088 self.runq_weight = self.calculate_task_weights(endpoints)
1089
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001090 self.init_progress_reporter.next_stage()
1091
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001092 # Sanity Check - Check for multiple tasks building the same provider
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001093 for mc in self.dataCaches:
1094 prov_list = {}
1095 seen_fn = []
1096 for tid in self.runtaskentries:
1097 (tidmc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1098 if taskfn in seen_fn:
1099 continue
1100 if mc != tidmc:
1101 continue
1102 seen_fn.append(taskfn)
1103 for prov in self.dataCaches[mc].fn_provides[taskfn]:
1104 if prov not in prov_list:
1105 prov_list[prov] = [taskfn]
1106 elif taskfn not in prov_list[prov]:
1107 prov_list[prov].append(taskfn)
1108 for prov in prov_list:
1109 if len(prov_list[prov]) < 2:
1110 continue
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001111 if prov in self.multi_provider_allowed:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001112 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001113 seen_pn = []
1114 # If two versions of the same PN are being built its fatal, we don't support it.
1115 for fn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001116 pn = self.dataCaches[mc].pkg_fn[fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001117 if pn not in seen_pn:
1118 seen_pn.append(pn)
1119 else:
1120 bb.fatal("Multiple versions of %s are due to be built (%s). Only one version of a given PN should be built in any given build. You likely need to set PREFERRED_VERSION_%s to select the correct version or don't depend on multiple versions." % (pn, " ".join(prov_list[prov]), pn))
Andrew Geissler595f6302022-01-24 19:11:47 +00001121 msgs = ["Multiple .bb files are due to be built which each provide %s:\n %s" % (prov, "\n ".join(prov_list[prov]))]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001122 #
1123 # Construct a list of things which uniquely depend on each provider
1124 # since this may help the user figure out which dependency is triggering this warning
1125 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001126 msgs.append("\nA list of tasks depending on these providers is shown and may help explain where the dependency comes from.")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001127 deplist = {}
1128 commondeps = None
1129 for provfn in prov_list[prov]:
1130 deps = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001131 for tid in self.runtaskentries:
1132 fn = fn_from_tid(tid)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001133 if fn != provfn:
1134 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001135 for dep in self.runtaskentries[tid].revdeps:
1136 fn = fn_from_tid(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001137 if fn == provfn:
1138 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001139 deps.add(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001140 if not commondeps:
1141 commondeps = set(deps)
1142 else:
1143 commondeps &= deps
1144 deplist[provfn] = deps
1145 for provfn in deplist:
Andrew Geissler595f6302022-01-24 19:11:47 +00001146 msgs.append("\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001147 #
1148 # Construct a list of provides and runtime providers for each recipe
1149 # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC)
1150 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001151 msgs.append("\nIt could be that one recipe provides something the other doesn't and should. The following provider and runtime provider differences may be helpful.")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001152 provide_results = {}
1153 rprovide_results = {}
1154 commonprovs = None
1155 commonrprovs = None
1156 for provfn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001157 provides = set(self.dataCaches[mc].fn_provides[provfn])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001158 rprovides = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001159 for rprovide in self.dataCaches[mc].rproviders:
1160 if provfn in self.dataCaches[mc].rproviders[rprovide]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001161 rprovides.add(rprovide)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001162 for package in self.dataCaches[mc].packages:
1163 if provfn in self.dataCaches[mc].packages[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001164 rprovides.add(package)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001165 for package in self.dataCaches[mc].packages_dynamic:
1166 if provfn in self.dataCaches[mc].packages_dynamic[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001167 rprovides.add(package)
1168 if not commonprovs:
1169 commonprovs = set(provides)
1170 else:
1171 commonprovs &= provides
1172 provide_results[provfn] = provides
1173 if not commonrprovs:
1174 commonrprovs = set(rprovides)
1175 else:
1176 commonrprovs &= rprovides
1177 rprovide_results[provfn] = rprovides
Andrew Geissler595f6302022-01-24 19:11:47 +00001178 #msgs.append("\nCommon provides:\n %s" % ("\n ".join(commonprovs)))
1179 #msgs.append("\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001180 for provfn in prov_list[prov]:
Andrew Geissler595f6302022-01-24 19:11:47 +00001181 msgs.append("\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs)))
1182 msgs.append("\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001183
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001184 if self.warn_multi_bb:
Andrew Geissler595f6302022-01-24 19:11:47 +00001185 logger.verbnote("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001186 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00001187 logger.error("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001188
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001189 self.init_progress_reporter.next_stage()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001190 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001191
1192 # Iterate over the task list looking for tasks with a 'setscene' function
Andrew Geissler82c905d2020-04-13 13:39:40 -05001193 self.runq_setscene_tids = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001194 if not self.cooker.configuration.nosetscene:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001195 for tid in self.runtaskentries:
1196 (mc, fn, taskname, _) = split_tid_mcfn(tid)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001197 setscenetid = tid + "_setscene"
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001198 if setscenetid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001199 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -05001200 self.runq_setscene_tids.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001201
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001202 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001203
1204 # Invalidate task if force mode active
1205 if self.cooker.configuration.force:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001206 for tid in self.target_tids:
1207 invalidate_task(tid, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001208
1209 # Invalidate task if invalidate mode active
1210 if self.cooker.configuration.invalidate_stamp:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001211 for tid in self.target_tids:
1212 fn = fn_from_tid(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001213 for st in self.cooker.configuration.invalidate_stamp.split(','):
1214 if not st.startswith("do_"):
1215 st = "do_%s" % st
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001216 invalidate_task(fn + ":" + st, True)
1217
1218 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001219
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001220 # Create and print to the logs a virtual/xxxx -> PN (fn) table
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001221 for mc in taskData:
1222 virtmap = taskData[mc].get_providermap(prefix="virtual/")
1223 virtpnmap = {}
1224 for v in virtmap:
1225 virtpnmap[v] = self.dataCaches[mc].pkg_fn[virtmap[v]]
1226 bb.debug(2, "%s resolved to: %s (%s)" % (v, virtpnmap[v], virtmap[v]))
1227 if hasattr(bb.parse.siggen, "tasks_resolved"):
1228 bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
1229
1230 self.init_progress_reporter.next_stage()
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001231
Brad Bishop00e122a2019-10-05 11:10:57 -04001232 bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
1233
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001234 # Iterate over the task list and call into the siggen code
1235 dealtwith = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001236 todeal = set(self.runtaskentries)
Andrew Geissler595f6302022-01-24 19:11:47 +00001237 while todeal:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001238 for tid in todeal.copy():
Andrew Geissler595f6302022-01-24 19:11:47 +00001239 if not (self.runtaskentries[tid].depends - dealtwith):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001240 dealtwith.add(tid)
1241 todeal.remove(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001242 self.prepare_task_hash(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001243
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001244 bb.parse.siggen.writeout_file_checksum_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001245
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001246 #self.dump_data()
1247 return len(self.runtaskentries)
1248
Brad Bishop19323692019-04-05 15:28:33 -04001249 def prepare_task_hash(self, tid):
Andrew Geissler517393d2023-01-13 08:55:19 -06001250 bb.parse.siggen.prep_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
1251 self.runtaskentries[tid].hash = bb.parse.siggen.get_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
Brad Bishop08902b02019-08-20 09:16:51 -04001252 self.runtaskentries[tid].unihash = bb.parse.siggen.get_unihash(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001253
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001254 def dump_data(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001255 """
1256 Dump some debug information on the internal data structures
1257 """
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001258 logger.debug3("run_tasks:")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001259 for tid in self.runtaskentries:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001260 logger.debug3(" %s: %s Deps %s RevDeps %s", tid,
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001261 self.runtaskentries[tid].weight,
1262 self.runtaskentries[tid].depends,
1263 self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001264
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001265class RunQueueWorker():
1266 def __init__(self, process, pipe):
1267 self.process = process
1268 self.pipe = pipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001269
1270class RunQueue:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001271 def __init__(self, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001272
1273 self.cooker = cooker
1274 self.cfgData = cfgData
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001275 self.rqdata = RunQueueData(self, cooker, cfgData, dataCaches, taskData, targets)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001276
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001277 self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION") or None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001278 self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID") or None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001279
1280 self.state = runQueuePrepare
1281
1282 # For disk space monitor
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001283 # Invoked at regular time intervals via the bitbake heartbeat event
1284 # while the build is running. We generate a unique name for the handler
1285 # here, just in case that there ever is more than one RunQueue instance,
Brad Bishop96ff1982019-08-19 13:50:42 -04001286 # start the handler when reaching runQueueSceneInit, and stop it when
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001287 # done with the build.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001288 self.dm = monitordisk.diskMonitor(cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001289 self.dm_event_handler_name = '_bb_diskmonitor_' + str(id(self))
1290 self.dm_event_handler_registered = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001291 self.rqexe = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001292 self.worker = {}
1293 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001294
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001295 def _start_worker(self, mc, fakeroot = False, rqexec = None):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001296 logger.debug("Starting bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001297 magic = "decafbad"
1298 if self.cooker.configuration.profile:
1299 magic = "decafbadbad"
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001300 fakerootlogs = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001301 if fakeroot:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001302 magic = magic + "beef"
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001303 mcdata = self.cooker.databuilder.mcdata[mc]
Brad Bishop19323692019-04-05 15:28:33 -04001304 fakerootcmd = shlex.split(mcdata.getVar("FAKEROOTCMD"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001305 fakerootenv = (mcdata.getVar("FAKEROOTBASEENV") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001306 env = os.environ.copy()
1307 for key, value in (var.split('=') for var in fakerootenv):
1308 env[key] = value
Brad Bishop19323692019-04-05 15:28:33 -04001309 worker = subprocess.Popen(fakerootcmd + ["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001310 fakerootlogs = self.rqdata.dataCaches[mc].fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001311 else:
1312 worker = subprocess.Popen(["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
1313 bb.utils.nonblockingfd(worker.stdout)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001314 workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, self, rqexec, fakerootlogs=fakerootlogs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001315
1316 workerdata = {
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001317 "sigdata" : bb.parse.siggen.get_taskdata(),
Andrew Geissler82c905d2020-04-13 13:39:40 -05001318 "logdefaultlevel" : bb.msg.loggerDefaultLogLevel,
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001319 "build_verbose_shell" : self.cooker.configuration.build_verbose_shell,
1320 "build_verbose_stdout" : self.cooker.configuration.build_verbose_stdout,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001321 "logdefaultdomain" : bb.msg.loggerDefaultDomains,
1322 "prhost" : self.cooker.prhost,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001323 "buildname" : self.cfgData.getVar("BUILDNAME"),
1324 "date" : self.cfgData.getVar("DATE"),
1325 "time" : self.cfgData.getVar("TIME"),
Brad Bishopa34c0302019-09-23 22:34:48 -04001326 "hashservaddr" : self.cooker.hashservaddr,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001327 "umask" : self.cfgData.getVar("BB_DEFAULT_UMASK"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001328 }
1329
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001330 worker.stdin.write(b"<cookerconfig>" + pickle.dumps(self.cooker.configuration) + b"</cookerconfig>")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001331 worker.stdin.write(b"<extraconfigdata>" + pickle.dumps(self.cooker.extraconfigdata) + b"</extraconfigdata>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001332 worker.stdin.write(b"<workerdata>" + pickle.dumps(workerdata) + b"</workerdata>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001333 worker.stdin.flush()
1334
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001335 return RunQueueWorker(worker, workerpipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001336
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001337 def _teardown_worker(self, worker):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001338 if not worker:
1339 return
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001340 logger.debug("Teardown for bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001341 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001342 worker.process.stdin.write(b"<quit></quit>")
1343 worker.process.stdin.flush()
1344 worker.process.stdin.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001345 except IOError:
1346 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001347 while worker.process.returncode is None:
1348 worker.pipe.read()
1349 worker.process.poll()
1350 while worker.pipe.read():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001351 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001352 worker.pipe.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001353
1354 def start_worker(self):
1355 if self.worker:
1356 self.teardown_workers()
1357 self.teardown = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001358 for mc in self.rqdata.dataCaches:
1359 self.worker[mc] = self._start_worker(mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001360
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001361 def start_fakeworker(self, rqexec, mc):
1362 if not mc in self.fakeworker:
1363 self.fakeworker[mc] = self._start_worker(mc, True, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001364
1365 def teardown_workers(self):
1366 self.teardown = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001367 for mc in self.worker:
1368 self._teardown_worker(self.worker[mc])
1369 self.worker = {}
1370 for mc in self.fakeworker:
1371 self._teardown_worker(self.fakeworker[mc])
1372 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001373
1374 def read_workers(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001375 for mc in self.worker:
1376 self.worker[mc].pipe.read()
1377 for mc in self.fakeworker:
1378 self.fakeworker[mc].pipe.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001379
1380 def active_fds(self):
1381 fds = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001382 for mc in self.worker:
1383 fds.append(self.worker[mc].pipe.input)
1384 for mc in self.fakeworker:
1385 fds.append(self.fakeworker[mc].pipe.input)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001386 return fds
1387
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001388 def check_stamp_task(self, tid, taskname = None, recurse = False, cache = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001389 def get_timestamp(f):
1390 try:
1391 if not os.access(f, os.F_OK):
1392 return None
1393 return os.stat(f)[stat.ST_MTIME]
1394 except:
1395 return None
1396
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001397 (mc, fn, tn, taskfn) = split_tid_mcfn(tid)
1398 if taskname is None:
1399 taskname = tn
1400
Andrew Geissler517393d2023-01-13 08:55:19 -06001401 stampfile = bb.parse.siggen.stampfile_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001402
1403 # If the stamp is missing, it's not current
1404 if not os.access(stampfile, os.F_OK):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001405 logger.debug2("Stampfile %s not available", stampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001406 return False
1407 # If it's a 'nostamp' task, it's not current
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001408 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001409 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001410 logger.debug2("%s.%s is nostamp\n", fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001411 return False
1412
Andrew Geissler517393d2023-01-13 08:55:19 -06001413 if taskname.endswith("_setscene"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001414 return True
1415
1416 if cache is None:
1417 cache = {}
1418
1419 iscurrent = True
1420 t1 = get_timestamp(stampfile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001421 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001422 if iscurrent:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001423 (mc2, fn2, taskname2, taskfn2) = split_tid_mcfn(dep)
Andrew Geissler517393d2023-01-13 08:55:19 -06001424 stampfile2 = bb.parse.siggen.stampfile_mcfn(taskname2, taskfn2)
1425 stampfile3 = bb.parse.siggen.stampfile_mcfn(taskname2 + "_setscene", taskfn2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001426 t2 = get_timestamp(stampfile2)
1427 t3 = get_timestamp(stampfile3)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001428 if t3 and not t2:
1429 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001430 if t3 and t3 > t2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001431 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00001432 if fn == fn2:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001433 if not t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001434 logger.debug2('Stampfile %s does not exist', stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001435 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001436 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001437 if t1 < t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001438 logger.debug2('Stampfile %s < %s', stampfile, stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001439 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001440 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001441 if recurse and iscurrent:
1442 if dep in cache:
1443 iscurrent = cache[dep]
1444 if not iscurrent:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001445 logger.debug2('Stampfile for dependency %s:%s invalid (cached)' % (fn2, taskname2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001446 else:
1447 iscurrent = self.check_stamp_task(dep, recurse=True, cache=cache)
1448 cache[dep] = iscurrent
1449 if recurse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001450 cache[tid] = iscurrent
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001451 return iscurrent
1452
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001453 def validate_hashes(self, tocheck, data, currentcount=0, siginfo=False, summary=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04001454 valid = set()
1455 if self.hashvalidate:
Brad Bishop08902b02019-08-20 09:16:51 -04001456 sq_data = {}
1457 sq_data['hash'] = {}
1458 sq_data['hashfn'] = {}
1459 sq_data['unihash'] = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04001460 for tid in tocheck:
1461 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04001462 sq_data['hash'][tid] = self.rqdata.runtaskentries[tid].hash
1463 sq_data['hashfn'][tid] = self.rqdata.dataCaches[mc].hashfn[taskfn]
1464 sq_data['unihash'][tid] = self.rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -04001465
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001466 valid = self.validate_hash(sq_data, data, siginfo, currentcount, summary)
Brad Bishop96ff1982019-08-19 13:50:42 -04001467
1468 return valid
1469
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001470 def validate_hash(self, sq_data, d, siginfo, currentcount, summary):
1471 locs = {"sq_data" : sq_data, "d" : d, "siginfo" : siginfo, "currentcount" : currentcount, "summary" : summary}
Brad Bishop19323692019-04-05 15:28:33 -04001472
Brad Bishop08902b02019-08-20 09:16:51 -04001473 # Metadata has **kwargs so args can be added, sq_data can also gain new fields
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001474 call = self.hashvalidate + "(sq_data, d, siginfo=siginfo, currentcount=currentcount, summary=summary)"
Brad Bishop19323692019-04-05 15:28:33 -04001475
Brad Bishop19323692019-04-05 15:28:33 -04001476 return bb.utils.better_eval(call, locs)
1477
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001478 def _execute_runqueue(self):
1479 """
1480 Run the tasks in a queue prepared by rqdata.prepare()
1481 Upon failure, optionally try to recover the build using any alternate providers
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001482 (if the halt on failure configuration option isn't set)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001483 """
1484
1485 retval = True
1486
1487 if self.state is runQueuePrepare:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001488 # NOTE: if you add, remove or significantly refactor the stages of this
1489 # process then you should recalculate the weightings here. This is quite
1490 # easy to do - just change the next line temporarily to pass debug=True as
1491 # the last parameter and you'll get a printout of the weightings as well
1492 # as a map to the lines where next_stage() was called. Of course this isn't
1493 # critical, but it helps to keep the progress reporting accurate.
1494 self.rqdata.init_progress_reporter = bb.progress.MultiStageProcessProgressReporter(self.cooker.data,
1495 "Initialising tasks",
1496 [43, 967, 4, 3, 1, 5, 3, 7, 13, 1, 2, 1, 1, 246, 35, 1, 38, 1, 35, 2, 338, 204, 142, 3, 3, 37, 244])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001497 if self.rqdata.prepare() == 0:
1498 self.state = runQueueComplete
1499 else:
1500 self.state = runQueueSceneInit
Brad Bishop00e122a2019-10-05 11:10:57 -04001501 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001502
1503 if self.state is runQueueSceneInit:
Brad Bishop96ff1982019-08-19 13:50:42 -04001504 self.rqdata.init_progress_reporter.next_stage()
1505
1506 # we are ready to run, emit dependency info to any UI or class which
1507 # needs it
1508 depgraph = self.cooker.buildDependTree(self, self.rqdata.taskData)
1509 self.rqdata.init_progress_reporter.next_stage()
1510 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
1511
Brad Bishope2d5b612018-11-23 10:55:50 +13001512 if not self.dm_event_handler_registered:
1513 res = bb.event.register(self.dm_event_handler_name,
Andrew Geissler517393d2023-01-13 08:55:19 -06001514 lambda x, y: self.dm.check(self) if self.state in [runQueueRunning, runQueueCleanUp] else False,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001515 ('bb.event.HeartbeatEvent',), data=self.cfgData)
Brad Bishope2d5b612018-11-23 10:55:50 +13001516 self.dm_event_handler_registered = True
1517
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001518 dump = self.cooker.configuration.dump_signatures
1519 if dump:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001520 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001521 if 'printdiff' in dump:
1522 invalidtasks = self.print_diffscenetasks()
1523 self.dump_signatures(dump)
1524 if 'printdiff' in dump:
1525 self.write_diffscenetasks(invalidtasks)
1526 self.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001527
Brad Bishop96ff1982019-08-19 13:50:42 -04001528 if self.state is runQueueSceneInit:
1529 self.rqdata.init_progress_reporter.next_stage()
1530 self.start_worker()
1531 self.rqdata.init_progress_reporter.next_stage()
1532 self.rqexe = RunQueueExecute(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001533
Brad Bishop96ff1982019-08-19 13:50:42 -04001534 # If we don't have any setscene functions, skip execution
Andrew Geissler595f6302022-01-24 19:11:47 +00001535 if not self.rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04001536 logger.info('No setscene tasks')
1537 for tid in self.rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00001538 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04001539 self.rqexe.setbuildable(tid)
1540 self.rqexe.tasks_notcovered.add(tid)
1541 self.rqexe.sqdone = True
1542 logger.info('Executing Tasks')
1543 self.state = runQueueRunning
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001544
1545 if self.state is runQueueRunning:
1546 retval = self.rqexe.execute()
1547
1548 if self.state is runQueueCleanUp:
1549 retval = self.rqexe.finish()
1550
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001551 build_done = self.state is runQueueComplete or self.state is runQueueFailed
1552
1553 if build_done and self.dm_event_handler_registered:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001554 bb.event.remove(self.dm_event_handler_name, None, data=self.cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001555 self.dm_event_handler_registered = False
1556
1557 if build_done and self.rqexe:
Brad Bishop08902b02019-08-20 09:16:51 -04001558 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001559 self.teardown_workers()
Brad Bishop96ff1982019-08-19 13:50:42 -04001560 if self.rqexe:
1561 if self.rqexe.stats.failed:
1562 logger.info("Tasks Summary: Attempted %d tasks of which %d didn't need to be rerun and %d failed.", self.rqexe.stats.completed + self.rqexe.stats.failed, self.rqexe.stats.skipped, self.rqexe.stats.failed)
1563 else:
1564 # Let's avoid the word "failed" if nothing actually did
1565 logger.info("Tasks Summary: Attempted %d tasks of which %d didn't need to be rerun and all succeeded.", self.rqexe.stats.completed, self.rqexe.stats.skipped)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001566
1567 if self.state is runQueueFailed:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001568 raise bb.runqueue.TaskFailure(self.rqexe.failed_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001569
1570 if self.state is runQueueComplete:
1571 # All done
1572 return False
1573
1574 # Loop
1575 return retval
1576
1577 def execute_runqueue(self):
1578 # Catch unexpected exceptions and ensure we exit when an error occurs, not loop.
1579 try:
1580 return self._execute_runqueue()
1581 except bb.runqueue.TaskFailure:
1582 raise
1583 except SystemExit:
1584 raise
1585 except bb.BBHandledException:
1586 try:
1587 self.teardown_workers()
1588 except:
1589 pass
1590 self.state = runQueueComplete
1591 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001592 except Exception as err:
1593 logger.exception("An uncaught exception occurred in runqueue")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001594 try:
1595 self.teardown_workers()
1596 except:
1597 pass
1598 self.state = runQueueComplete
1599 raise
1600
1601 def finish_runqueue(self, now = False):
1602 if not self.rqexe:
1603 self.state = runQueueComplete
1604 return
1605
1606 if now:
1607 self.rqexe.finish_now()
1608 else:
1609 self.rqexe.finish()
1610
Andrew Geissler517393d2023-01-13 08:55:19 -06001611 def _rq_dump_sigtid(self, tids):
1612 for tid in tids:
1613 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1614 dataCaches = self.rqdata.dataCaches
1615 bb.parse.siggen.dump_sigtask(taskfn, taskname, dataCaches[mc].stamp[taskfn], True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001616
1617 def dump_signatures(self, options):
Andrew Geissler517393d2023-01-13 08:55:19 -06001618 if bb.cooker.CookerFeatures.RECIPE_SIGGEN_INFO not in self.cooker.featureset:
1619 bb.fatal("The dump signatures functionality needs the RECIPE_SIGGEN_INFO feature enabled")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001620
Andrew Geissler517393d2023-01-13 08:55:19 -06001621 bb.note("Writing task signature files")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001622
1623 max_process = int(self.cfgData.getVar("BB_NUMBER_PARSE_THREADS") or os.cpu_count() or 1)
Andrew Geissler517393d2023-01-13 08:55:19 -06001624 def chunkify(l, n):
1625 return [l[i::n] for i in range(n)]
1626 tids = chunkify(list(self.rqdata.runtaskentries), max_process)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001627 # We cannot use the real multiprocessing.Pool easily due to some local data
1628 # that can't be pickled. This is a cheap multi-process solution.
1629 launched = []
Andrew Geissler517393d2023-01-13 08:55:19 -06001630 while tids:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001631 if len(launched) < max_process:
Andrew Geissler517393d2023-01-13 08:55:19 -06001632 p = Process(target=self._rq_dump_sigtid, args=(tids.pop(), ))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001633 p.start()
1634 launched.append(p)
1635 for q in launched:
1636 # The finished processes are joined when calling is_alive()
1637 if not q.is_alive():
1638 launched.remove(q)
1639 for p in launched:
1640 p.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001641
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001642 bb.parse.siggen.dump_sigs(self.rqdata.dataCaches, options)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001643
1644 return
1645
1646 def print_diffscenetasks(self):
1647
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001648 noexec = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001649 tocheck = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001650
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001651 for tid in self.rqdata.runtaskentries:
1652 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1653 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001654
1655 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001656 noexec.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001657 continue
1658
Brad Bishop96ff1982019-08-19 13:50:42 -04001659 tocheck.add(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001660
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001661 valid_new = self.validate_hashes(tocheck, self.cooker.data, 0, True, summary=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001662
1663 # Tasks which are both setscene and noexec never care about dependencies
1664 # We therefore find tasks which are setscene and noexec and mark their
1665 # unique dependencies as valid.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001666 for tid in noexec:
1667 if tid not in self.rqdata.runq_setscene_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001668 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001669 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001670 hasnoexecparents = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001671 for dep2 in self.rqdata.runtaskentries[dep].revdeps:
1672 if dep2 in self.rqdata.runq_setscene_tids and dep2 in noexec:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001673 continue
1674 hasnoexecparents = False
1675 break
1676 if hasnoexecparents:
1677 valid_new.add(dep)
1678
1679 invalidtasks = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001680 for tid in self.rqdata.runtaskentries:
1681 if tid not in valid_new and tid not in noexec:
1682 invalidtasks.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001683
1684 found = set()
1685 processed = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001686 for tid in invalidtasks:
1687 toprocess = set([tid])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001688 while toprocess:
1689 next = set()
1690 for t in toprocess:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001691 for dep in self.rqdata.runtaskentries[t].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001692 if dep in invalidtasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001693 found.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001694 if dep not in processed:
1695 processed.add(dep)
1696 next.add(dep)
1697 toprocess = next
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001698 if tid in found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001699 toprocess = set()
1700
1701 tasklist = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001702 for tid in invalidtasks.difference(found):
1703 tasklist.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001704
1705 if tasklist:
1706 bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
1707
1708 return invalidtasks.difference(found)
1709
1710 def write_diffscenetasks(self, invalidtasks):
1711
1712 # Define recursion callback
1713 def recursecb(key, hash1, hash2):
1714 hashes = [hash1, hash2]
1715 hashfiles = bb.siggen.find_siginfo(key, None, hashes, self.cfgData)
1716
1717 recout = []
1718 if len(hashfiles) == 2:
1719 out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
Brad Bishopc342db32019-05-15 21:57:59 -04001720 recout.extend(list(' ' + l for l in out2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001721 else:
1722 recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
1723
1724 return recout
1725
1726
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001727 for tid in invalidtasks:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001728 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1729 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001730 h = self.rqdata.runtaskentries[tid].hash
Patrick Williams03907ee2022-05-01 06:28:52 -05001731 matches = bb.siggen.find_siginfo(pn, taskname, [], self.cooker.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001732 match = None
1733 for m in matches:
1734 if h in m:
1735 match = m
1736 if match is None:
1737 bb.fatal("Can't find a task we're supposed to have written out? (hash: %s)?" % h)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001738 matches = {k : v for k, v in iter(matches.items()) if h not in k}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001739 if matches:
1740 latestmatch = sorted(matches.keys(), key=lambda f: matches[f])[-1]
Brad Bishop19323692019-04-05 15:28:33 -04001741 prevh = __find_sha256__.search(latestmatch).group(0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001742 output = bb.siggen.compare_sigfiles(latestmatch, match, recursecb)
1743 bb.plain("\nTask %s:%s couldn't be used from the cache because:\n We need hash %s, closest matching task was %s\n " % (pn, taskname, h, prevh) + '\n '.join(output))
1744
Brad Bishop96ff1982019-08-19 13:50:42 -04001745
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001746class RunQueueExecute:
1747
1748 def __init__(self, rq):
1749 self.rq = rq
1750 self.cooker = rq.cooker
1751 self.cfgData = rq.cfgData
1752 self.rqdata = rq.rqdata
1753
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001754 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
1755 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001756 self.max_cpu_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_CPU")
1757 self.max_io_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_IO")
Patrick Williams92b42cb2022-09-03 06:53:57 -05001758 self.max_memory_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_MEMORY")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001759
Brad Bishop96ff1982019-08-19 13:50:42 -04001760 self.sq_buildable = set()
1761 self.sq_running = set()
1762 self.sq_live = set()
1763
Brad Bishop08902b02019-08-20 09:16:51 -04001764 self.updated_taskhash_queue = []
1765 self.pending_migrations = set()
1766
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001767 self.runq_buildable = set()
1768 self.runq_running = set()
1769 self.runq_complete = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001770 self.runq_tasksrun = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001771
1772 self.build_stamps = {}
1773 self.build_stamps2 = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001774 self.failed_tids = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001775 self.sq_deferred = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001776
1777 self.stampcache = {}
1778
Brad Bishop08902b02019-08-20 09:16:51 -04001779 self.holdoff_tasks = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04001780 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04001781 self.sqdone = False
1782
Andrew Geissler5199d832021-09-24 16:47:35 -05001783 self.stats = RunQueueStats(len(self.rqdata.runtaskentries), len(self.rqdata.runq_setscene_tids))
Brad Bishop96ff1982019-08-19 13:50:42 -04001784
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001785 for mc in rq.worker:
1786 rq.worker[mc].pipe.setrunqueueexec(self)
1787 for mc in rq.fakeworker:
1788 rq.fakeworker[mc].pipe.setrunqueueexec(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001789
1790 if self.number_tasks <= 0:
1791 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks)
1792
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001793 lower_limit = 1.0
1794 upper_limit = 1000000.0
1795 if self.max_cpu_pressure:
1796 self.max_cpu_pressure = float(self.max_cpu_pressure)
1797 if self.max_cpu_pressure < lower_limit:
1798 bb.fatal("Invalid BB_PRESSURE_MAX_CPU %s, minimum value is %s." % (self.max_cpu_pressure, lower_limit))
1799 if self.max_cpu_pressure > upper_limit:
1800 bb.warn("Your build will be largely unregulated since BB_PRESSURE_MAX_CPU is set to %s. It is very unlikely that such high pressure will be experienced." % (self.max_cpu_pressure))
1801
1802 if self.max_io_pressure:
1803 self.max_io_pressure = float(self.max_io_pressure)
1804 if self.max_io_pressure < lower_limit:
1805 bb.fatal("Invalid BB_PRESSURE_MAX_IO %s, minimum value is %s." % (self.max_io_pressure, lower_limit))
1806 if self.max_io_pressure > upper_limit:
1807 bb.warn("Your build will be largely unregulated since BB_PRESSURE_MAX_IO is set to %s. It is very unlikely that such high pressure will be experienced." % (self.max_io_pressure))
1808
Patrick Williams92b42cb2022-09-03 06:53:57 -05001809 if self.max_memory_pressure:
1810 self.max_memory_pressure = float(self.max_memory_pressure)
1811 if self.max_memory_pressure < lower_limit:
1812 bb.fatal("Invalid BB_PRESSURE_MAX_MEMORY %s, minimum value is %s." % (self.max_memory_pressure, lower_limit))
1813 if self.max_memory_pressure > upper_limit:
1814 bb.warn("Your build will be largely unregulated since BB_PRESSURE_MAX_MEMORY is set to %s. It is very unlikely that such high pressure will be experienced." % (self.max_io_pressure))
1815
Brad Bishop96ff1982019-08-19 13:50:42 -04001816 # List of setscene tasks which we've covered
1817 self.scenequeue_covered = set()
1818 # List of tasks which are covered (including setscene ones)
1819 self.tasks_covered = set()
1820 self.tasks_scenequeue_done = set()
1821 self.scenequeue_notcovered = set()
1822 self.tasks_notcovered = set()
1823 self.scenequeue_notneeded = set()
1824
Brad Bishop08902b02019-08-20 09:16:51 -04001825 # We can't skip specified target tasks which aren't setscene tasks
1826 self.cantskip = set(self.rqdata.target_tids)
1827 self.cantskip.difference_update(self.rqdata.runq_setscene_tids)
1828 self.cantskip.intersection_update(self.rqdata.runtaskentries)
Brad Bishop96ff1982019-08-19 13:50:42 -04001829
1830 schedulers = self.get_schedulers()
1831 for scheduler in schedulers:
1832 if self.scheduler == scheduler.name:
1833 self.sched = scheduler(self, self.rqdata)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001834 logger.debug("Using runqueue scheduler '%s'", scheduler.name)
Brad Bishop96ff1982019-08-19 13:50:42 -04001835 break
1836 else:
1837 bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
1838 (self.scheduler, ", ".join(obj.name for obj in schedulers)))
1839
Andrew Geissler595f6302022-01-24 19:11:47 +00001840 #if self.rqdata.runq_setscene_tids:
Brad Bishop08902b02019-08-20 09:16:51 -04001841 self.sqdata = SQData()
1842 build_scenequeue_data(self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self)
Brad Bishop96ff1982019-08-19 13:50:42 -04001843
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001844 def runqueue_process_waitpid(self, task, status, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001845
1846 # self.build_stamps[pid] may not exist when use shared work directory.
1847 if task in self.build_stamps:
1848 self.build_stamps2.remove(self.build_stamps[task])
1849 del self.build_stamps[task]
1850
Brad Bishop96ff1982019-08-19 13:50:42 -04001851 if task in self.sq_live:
1852 if status != 0:
1853 self.sq_task_fail(task, status)
1854 else:
1855 self.sq_task_complete(task)
1856 self.sq_live.remove(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05001857 self.stats.updateActiveSetscene(len(self.sq_live))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001858 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04001859 if status != 0:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001860 self.task_fail(task, status, fakerootlog=fakerootlog)
Brad Bishop96ff1982019-08-19 13:50:42 -04001861 else:
1862 self.task_complete(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001863 return True
1864
1865 def finish_now(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001866 for mc in self.rq.worker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001867 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001868 self.rq.worker[mc].process.stdin.write(b"<finishnow></finishnow>")
1869 self.rq.worker[mc].process.stdin.flush()
1870 except IOError:
1871 # worker must have died?
1872 pass
1873 for mc in self.rq.fakeworker:
1874 try:
1875 self.rq.fakeworker[mc].process.stdin.write(b"<finishnow></finishnow>")
1876 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001877 except IOError:
1878 # worker must have died?
1879 pass
1880
Andrew Geissler595f6302022-01-24 19:11:47 +00001881 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001882 self.rq.state = runQueueFailed
1883 return
1884
1885 self.rq.state = runQueueComplete
1886 return
1887
1888 def finish(self):
1889 self.rq.state = runQueueCleanUp
1890
Andrew Geissler5199d832021-09-24 16:47:35 -05001891 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001892 if active > 0:
1893 bb.event.fire(runQueueExitWait(active), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001894 self.rq.read_workers()
1895 return self.rq.active_fds()
1896
Andrew Geissler595f6302022-01-24 19:11:47 +00001897 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001898 self.rq.state = runQueueFailed
1899 return True
1900
1901 self.rq.state = runQueueComplete
1902 return True
1903
Brad Bishop96ff1982019-08-19 13:50:42 -04001904 # Used by setscene only
1905 def check_dependencies(self, task, taskdeps):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001906 if not self.rq.depvalidate:
1907 return False
1908
Brad Bishop08902b02019-08-20 09:16:51 -04001909 # Must not edit parent data
1910 taskdeps = set(taskdeps)
1911
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001912 taskdata = {}
1913 taskdeps.add(task)
1914 for dep in taskdeps:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001915 (mc, fn, taskname, taskfn) = split_tid_mcfn(dep)
1916 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001917 taskdata[dep] = [pn, taskname, fn]
1918 call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001919 locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001920 valid = bb.utils.better_eval(call, locs)
1921 return valid
1922
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001923 def can_start_task(self):
Andrew Geissler5199d832021-09-24 16:47:35 -05001924 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001925 can_start = active < self.number_tasks
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001926 return can_start
1927
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001928 def get_schedulers(self):
1929 schedulers = set(obj for obj in globals().values()
1930 if type(obj) is type and
1931 issubclass(obj, RunQueueScheduler))
1932
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001933 user_schedulers = self.cfgData.getVar("BB_SCHEDULERS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001934 if user_schedulers:
1935 for sched in user_schedulers.split():
1936 if not "." in sched:
1937 bb.note("Ignoring scheduler '%s' from BB_SCHEDULERS: not an import" % sched)
1938 continue
1939
1940 modname, name = sched.rsplit(".", 1)
1941 try:
1942 module = __import__(modname, fromlist=(name,))
1943 except ImportError as exc:
1944 logger.critical("Unable to import scheduler '%s' from '%s': %s" % (name, modname, exc))
1945 raise SystemExit(1)
1946 else:
1947 schedulers.add(getattr(module, name))
1948 return schedulers
1949
1950 def setbuildable(self, task):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001951 self.runq_buildable.add(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001952 self.sched.newbuildable(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001953
1954 def task_completeoutright(self, task):
1955 """
1956 Mark a task as completed
1957 Look at the reverse dependencies and mark any task with
1958 completed dependencies as buildable
1959 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001960 self.runq_complete.add(task)
1961 for revdep in self.rqdata.runtaskentries[task].revdeps:
1962 if revdep in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001963 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001964 if revdep in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001965 continue
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001966 alldeps = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001967 for dep in self.rqdata.runtaskentries[revdep].depends:
1968 if dep not in self.runq_complete:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001969 alldeps = False
1970 break
1971 if alldeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001972 self.setbuildable(revdep)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001973 logger.debug("Marking task %s as buildable", revdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001974
Andrew Geissler5199d832021-09-24 16:47:35 -05001975 for t in self.sq_deferred.copy():
1976 if self.sq_deferred[t] == task:
1977 logger.debug2("Deferred task %s now buildable" % t)
1978 del self.sq_deferred[t]
1979 update_scenequeue_data([t], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
1980
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001981 def task_complete(self, task):
1982 self.stats.taskCompleted()
1983 bb.event.fire(runQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
1984 self.task_completeoutright(task)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001985 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001986
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001987 def task_fail(self, task, exitcode, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001988 """
1989 Called when a task has failed
1990 Updates the state engine with the failure
1991 """
1992 self.stats.taskFailed()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001993 self.failed_tids.append(task)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001994
Andrew Geissler595f6302022-01-24 19:11:47 +00001995 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001996 if fakerootlog and os.path.exists(fakerootlog):
1997 with open(fakerootlog) as fakeroot_log_file:
1998 fakeroot_failed = False
1999 for line in reversed(fakeroot_log_file.readlines()):
2000 for fakeroot_error in ['mismatch', 'error', 'fatal']:
2001 if fakeroot_error in line.lower():
2002 fakeroot_failed = True
2003 if 'doing new pid setup and server start' in line:
2004 break
Andrew Geissler595f6302022-01-24 19:11:47 +00002005 fakeroot_log.append(line)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002006
2007 if not fakeroot_failed:
Andrew Geissler595f6302022-01-24 19:11:47 +00002008 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002009
Andrew Geissler595f6302022-01-24 19:11:47 +00002010 bb.event.fire(runQueueTaskFailed(task, self.stats, exitcode, self.rq, fakeroot_log=("".join(fakeroot_log) or None)), self.cfgData)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002011
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002012 if self.rqdata.taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002013 self.rq.state = runQueueCleanUp
2014
2015 def task_skip(self, task, reason):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002016 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002017 self.setbuildable(task)
2018 bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
2019 self.task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002020 self.stats.taskSkipped()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002021 self.stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002022
Brad Bishop08902b02019-08-20 09:16:51 -04002023 def summarise_scenequeue_errors(self):
2024 err = False
2025 if not self.sqdone:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002026 logger.debug('We could skip tasks %s', "\n".join(sorted(self.scenequeue_covered)))
Andrew Geissler5199d832021-09-24 16:47:35 -05002027 completeevent = sceneQueueComplete(self.stats, self.rq)
Brad Bishop08902b02019-08-20 09:16:51 -04002028 bb.event.fire(completeevent, self.cfgData)
2029 if self.sq_deferred:
2030 logger.error("Scenequeue had deferred entries: %s" % pprint.pformat(self.sq_deferred))
2031 err = True
2032 if self.updated_taskhash_queue:
2033 logger.error("Scenequeue had unprocessed changed taskhash entries: %s" % pprint.pformat(self.updated_taskhash_queue))
2034 err = True
2035 if self.holdoff_tasks:
2036 logger.error("Scenequeue had holdoff tasks: %s" % pprint.pformat(self.holdoff_tasks))
2037 err = True
2038
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002039 for tid in self.scenequeue_covered.intersection(self.scenequeue_notcovered):
2040 # No task should end up in both covered and uncovered, that is a bug.
2041 logger.error("Setscene task %s in both covered and notcovered." % tid)
2042
Brad Bishop08902b02019-08-20 09:16:51 -04002043 for tid in self.rqdata.runq_setscene_tids:
2044 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2045 err = True
2046 logger.error("Setscene Task %s was never marked as covered or not covered" % tid)
2047 if tid not in self.sq_buildable:
2048 err = True
2049 logger.error("Setscene Task %s was never marked as buildable" % tid)
2050 if tid not in self.sq_running:
2051 err = True
2052 logger.error("Setscene Task %s was never marked as running" % tid)
2053
2054 for x in self.rqdata.runtaskentries:
2055 if x not in self.tasks_covered and x not in self.tasks_notcovered:
2056 logger.error("Task %s was never moved from the setscene queue" % x)
2057 err = True
2058 if x not in self.tasks_scenequeue_done:
2059 logger.error("Task %s was never processed by the setscene code" % x)
2060 err = True
Andrew Geissler595f6302022-01-24 19:11:47 +00002061 if not self.rqdata.runtaskentries[x].depends and x not in self.runq_buildable:
Brad Bishop08902b02019-08-20 09:16:51 -04002062 logger.error("Task %s was never marked as buildable by the setscene code" % x)
2063 err = True
2064 return err
2065
2066
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002067 def execute(self):
2068 """
Brad Bishop96ff1982019-08-19 13:50:42 -04002069 Run the tasks in a queue prepared by prepare_runqueue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002070 """
2071
2072 self.rq.read_workers()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002073 if self.updated_taskhash_queue or self.pending_migrations:
2074 self.process_possible_migrations()
2075
2076 if not hasattr(self, "sorted_setscene_tids"):
2077 # Don't want to sort this set every execution
2078 self.sorted_setscene_tids = sorted(self.rqdata.runq_setscene_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002079
Brad Bishop96ff1982019-08-19 13:50:42 -04002080 task = None
2081 if not self.sqdone and self.can_start_task():
2082 # Find the next setscene to run
Andrew Geissler82c905d2020-04-13 13:39:40 -05002083 for nexttask in self.sorted_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002084 if nexttask in self.sq_buildable and nexttask not in self.sq_running and self.sqdata.stamps[nexttask] not in self.build_stamps.values():
Andrew Geissler595f6302022-01-24 19:11:47 +00002085 if nexttask not in self.sqdata.unskippable and self.sqdata.sq_revdeps[nexttask] and self.sqdata.sq_revdeps[nexttask].issubset(self.scenequeue_covered) and self.check_dependencies(nexttask, self.sqdata.sq_revdeps[nexttask]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002086 if nexttask not in self.rqdata.target_tids:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002087 logger.debug2("Skipping setscene for task %s" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002088 self.sq_task_skip(nexttask)
2089 self.scenequeue_notneeded.add(nexttask)
2090 if nexttask in self.sq_deferred:
2091 del self.sq_deferred[nexttask]
2092 return True
Brad Bishop08902b02019-08-20 09:16:51 -04002093 # If covered tasks are running, need to wait for them to complete
2094 for t in self.sqdata.sq_covered_tasks[nexttask]:
2095 if t in self.runq_running and t not in self.runq_complete:
2096 continue
Brad Bishop96ff1982019-08-19 13:50:42 -04002097 if nexttask in self.sq_deferred:
2098 if self.sq_deferred[nexttask] not in self.runq_complete:
2099 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002100 logger.debug("Task %s no longer deferred" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002101 del self.sq_deferred[nexttask]
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002102 valid = self.rq.validate_hashes(set([nexttask]), self.cooker.data, 0, False, summary=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002103 if not valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002104 logger.debug("%s didn't become valid, skipping setscene" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002105 self.sq_task_failoutright(nexttask)
2106 return True
Brad Bishop96ff1982019-08-19 13:50:42 -04002107 if nexttask in self.sqdata.outrightfail:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002108 logger.debug2('No package found, so skipping setscene task %s', nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002109 self.sq_task_failoutright(nexttask)
2110 return True
2111 if nexttask in self.sqdata.unskippable:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002112 logger.debug2("Setscene task %s is unskippable" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002113 task = nexttask
2114 break
2115 if task is not None:
2116 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
2117 taskname = taskname + "_setscene"
2118 if self.rq.check_stamp_task(task, taskname_from_tid(task), recurse = True, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002119 logger.debug2('Stamp for underlying task %s is current, so skipping setscene variant', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002120 self.sq_task_failoutright(task)
2121 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002122
Brad Bishop96ff1982019-08-19 13:50:42 -04002123 if self.cooker.configuration.force:
2124 if task in self.rqdata.target_tids:
2125 self.sq_task_failoutright(task)
2126 return True
2127
2128 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002129 logger.debug2('Setscene stamp current task %s, so skip it and its dependencies', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002130 self.sq_task_skip(task)
2131 return True
2132
2133 if self.cooker.configuration.skipsetscene:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002134 logger.debug2('No setscene tasks should be executed. Skipping %s', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002135 self.sq_task_failoutright(task)
2136 return True
2137
Andrew Geissler5199d832021-09-24 16:47:35 -05002138 startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
Brad Bishop96ff1982019-08-19 13:50:42 -04002139 bb.event.fire(startevent, self.cfgData)
2140
Brad Bishop96ff1982019-08-19 13:50:42 -04002141 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Andrew Geissler517393d2023-01-13 08:55:19 -06002142 runtask = {
2143 'fn' : taskfn,
2144 'task' : task,
2145 'taskname' : taskname,
2146 'taskhash' : self.rqdata.get_task_hash(task),
2147 'unihash' : self.rqdata.get_task_unihash(task),
2148 'quieterrors' : True,
2149 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
2150 'taskdepdata' : self.sq_build_taskdepdata(task),
2151 'dry_run' : False,
2152 'taskdep': taskdep,
2153 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2154 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2155 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2156 }
2157
Brad Bishop96ff1982019-08-19 13:50:42 -04002158 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
2159 if not mc in self.rq.fakeworker:
2160 self.rq.start_fakeworker(self, mc)
Andrew Geissler517393d2023-01-13 08:55:19 -06002161 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002162 self.rq.fakeworker[mc].process.stdin.flush()
2163 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06002164 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002165 self.rq.worker[mc].process.stdin.flush()
2166
Andrew Geissler517393d2023-01-13 08:55:19 -06002167 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002168 self.build_stamps2.append(self.build_stamps[task])
2169 self.sq_running.add(task)
2170 self.sq_live.add(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05002171 self.stats.updateActiveSetscene(len(self.sq_live))
Brad Bishop96ff1982019-08-19 13:50:42 -04002172 if self.can_start_task():
2173 return True
2174
Brad Bishopc68388fc2019-08-26 01:33:31 -04002175 self.update_holdofftasks()
2176
Brad Bishop08902b02019-08-20 09:16:51 -04002177 if not self.sq_live and not self.sqdone and not self.sq_deferred and not self.updated_taskhash_queue and not self.holdoff_tasks:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002178 hashequiv_logger.verbose("Setscene tasks completed")
Brad Bishop96ff1982019-08-19 13:50:42 -04002179
Brad Bishop08902b02019-08-20 09:16:51 -04002180 err = self.summarise_scenequeue_errors()
Brad Bishop96ff1982019-08-19 13:50:42 -04002181 if err:
2182 self.rq.state = runQueueFailed
2183 return True
2184
2185 if self.cooker.configuration.setsceneonly:
2186 self.rq.state = runQueueComplete
2187 return True
2188 self.sqdone = True
2189
2190 if self.stats.total == 0:
2191 # nothing to do
2192 self.rq.state = runQueueComplete
2193 return True
2194
2195 if self.cooker.configuration.setsceneonly:
2196 task = None
2197 else:
2198 task = self.sched.next()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002199 if task is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002200 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002201
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002202 if self.rqdata.setscene_ignore_tasks is not None:
2203 if self.check_setscene_ignore_tasks(task):
2204 self.task_fail(task, "setscene ignore_tasks")
Brad Bishop96ff1982019-08-19 13:50:42 -04002205 return True
2206
2207 if task in self.tasks_covered:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002208 logger.debug2("Setscene covered task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002209 self.task_skip(task, "covered")
2210 return True
2211
2212 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002213 logger.debug2("Stamp current task %s", task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002214
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002215 self.task_skip(task, "existing")
Andrew Geissler82c905d2020-04-13 13:39:40 -05002216 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002217 return True
2218
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002219 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002220 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2221 startevent = runQueueTaskStarted(task, self.stats, self.rq,
2222 noexec=True)
2223 bb.event.fire(startevent, self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002224 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002225 self.stats.taskActive()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002226 if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Andrew Geissler517393d2023-01-13 08:55:19 -06002227 bb.build.make_stamp_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002228 self.task_complete(task)
2229 return True
2230 else:
2231 startevent = runQueueTaskStarted(task, self.stats, self.rq)
2232 bb.event.fire(startevent, self.cfgData)
2233
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002234 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Andrew Geissler517393d2023-01-13 08:55:19 -06002235 runtask = {
2236 'fn' : taskfn,
2237 'task' : task,
2238 'taskname' : taskname,
2239 'taskhash' : self.rqdata.get_task_hash(task),
2240 'unihash' : self.rqdata.get_task_unihash(task),
2241 'quieterrors' : False,
2242 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
2243 'taskdepdata' : self.build_taskdepdata(task),
2244 'dry_run' : self.rqdata.setscene_enforce,
2245 'taskdep': taskdep,
2246 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2247 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2248 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2249 }
2250
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002251 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002252 if not mc in self.rq.fakeworker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002253 try:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002254 self.rq.start_fakeworker(self, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002255 except OSError as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002256 logger.critical("Failed to spawn fakeroot worker to run %s: %s" % (task, str(exc)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002257 self.rq.state = runQueueFailed
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002258 self.stats.taskFailed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002259 return True
Andrew Geissler517393d2023-01-13 08:55:19 -06002260 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002261 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002262 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06002263 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002264 self.rq.worker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002265
Andrew Geissler517393d2023-01-13 08:55:19 -06002266 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002267 self.build_stamps2.append(self.build_stamps[task])
2268 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002269 self.stats.taskActive()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002270 if self.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002271 return True
2272
Andrew Geissler595f6302022-01-24 19:11:47 +00002273 if self.stats.active > 0 or self.sq_live:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002274 self.rq.read_workers()
2275 return self.rq.active_fds()
2276
Brad Bishop96ff1982019-08-19 13:50:42 -04002277 # No more tasks can be run. If we have deferred setscene tasks we should run them.
2278 if self.sq_deferred:
Patrick Williams92b42cb2022-09-03 06:53:57 -05002279 deferred_tid = list(self.sq_deferred.keys())[0]
2280 blocking_tid = self.sq_deferred.pop(deferred_tid)
Patrick Williams2390b1b2022-11-03 13:47:49 -05002281 logger.warning("Runqueue deadlocked on deferred tasks, forcing task %s blocked by %s" % (deferred_tid, blocking_tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04002282 return True
2283
Andrew Geissler595f6302022-01-24 19:11:47 +00002284 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002285 self.rq.state = runQueueFailed
2286 return True
2287
2288 # Sanity Checks
Brad Bishop08902b02019-08-20 09:16:51 -04002289 err = self.summarise_scenequeue_errors()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002290 for task in self.rqdata.runtaskentries:
2291 if task not in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002292 logger.error("Task %s never buildable!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002293 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002294 elif task not in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002295 logger.error("Task %s never ran!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002296 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002297 elif task not in self.runq_complete:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002298 logger.error("Task %s never completed!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002299 err = True
2300
2301 if err:
2302 self.rq.state = runQueueFailed
2303 else:
2304 self.rq.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002305
2306 return True
2307
Brad Bishopc68388fc2019-08-26 01:33:31 -04002308 def filtermcdeps(self, task, mc, deps):
Andrew Geissler99467da2019-02-25 18:54:23 -06002309 ret = set()
Andrew Geissler99467da2019-02-25 18:54:23 -06002310 for dep in deps:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002311 thismc = mc_from_tid(dep)
2312 if thismc != mc:
Andrew Geissler99467da2019-02-25 18:54:23 -06002313 continue
2314 ret.add(dep)
2315 return ret
2316
Brad Bishopa34c0302019-09-23 22:34:48 -04002317 # We filter out multiconfig dependencies from taskdepdata we pass to the tasks
Andrew Geissler99467da2019-02-25 18:54:23 -06002318 # as most code can't handle them
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002319 def build_taskdepdata(self, task):
2320 taskdepdata = {}
Brad Bishopc68388fc2019-08-26 01:33:31 -04002321 mc = mc_from_tid(task)
Brad Bishop08902b02019-08-20 09:16:51 -04002322 next = self.rqdata.runtaskentries[task].depends.copy()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002323 next.add(task)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002324 next = self.filtermcdeps(task, mc, next)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002325 while next:
2326 additional = []
2327 for revdep in next:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002328 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2329 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2330 deps = self.rqdata.runtaskentries[revdep].depends
2331 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002332 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002333 unihash = self.rqdata.runtaskentries[revdep].unihash
Brad Bishopc68388fc2019-08-26 01:33:31 -04002334 deps = self.filtermcdeps(task, mc, deps)
Brad Bishop19323692019-04-05 15:28:33 -04002335 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002336 for revdep2 in deps:
2337 if revdep2 not in taskdepdata:
2338 additional.append(revdep2)
2339 next = additional
2340
2341 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2342 return taskdepdata
2343
Brad Bishop08902b02019-08-20 09:16:51 -04002344 def update_holdofftasks(self):
Brad Bishopc68388fc2019-08-26 01:33:31 -04002345
2346 if not self.holdoff_need_update:
2347 return
2348
2349 notcovered = set(self.scenequeue_notcovered)
2350 notcovered |= self.cantskip
2351 for tid in self.scenequeue_notcovered:
2352 notcovered |= self.sqdata.sq_covered_tasks[tid]
2353 notcovered |= self.sqdata.unskippable.difference(self.rqdata.runq_setscene_tids)
2354 notcovered.intersection_update(self.tasks_scenequeue_done)
2355
2356 covered = set(self.scenequeue_covered)
2357 for tid in self.scenequeue_covered:
2358 covered |= self.sqdata.sq_covered_tasks[tid]
2359 covered.difference_update(notcovered)
2360 covered.intersection_update(self.tasks_scenequeue_done)
2361
2362 for tid in notcovered | covered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002363 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002364 self.setbuildable(tid)
2365 elif self.rqdata.runtaskentries[tid].depends.issubset(self.runq_complete):
2366 self.setbuildable(tid)
2367
2368 self.tasks_covered = covered
2369 self.tasks_notcovered = notcovered
2370
Brad Bishop08902b02019-08-20 09:16:51 -04002371 self.holdoff_tasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002372
Brad Bishop08902b02019-08-20 09:16:51 -04002373 for tid in self.rqdata.runq_setscene_tids:
2374 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2375 self.holdoff_tasks.add(tid)
2376
2377 for tid in self.holdoff_tasks.copy():
2378 for dep in self.sqdata.sq_covered_tasks[tid]:
2379 if dep not in self.runq_complete:
2380 self.holdoff_tasks.add(dep)
2381
Brad Bishopc68388fc2019-08-26 01:33:31 -04002382 self.holdoff_need_update = False
2383
Brad Bishop08902b02019-08-20 09:16:51 -04002384 def process_possible_migrations(self):
2385
2386 changed = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002387 toprocess = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002388 for tid, unihash in self.updated_taskhash_queue.copy():
2389 if tid in self.runq_running and tid not in self.runq_complete:
2390 continue
2391
2392 self.updated_taskhash_queue.remove((tid, unihash))
2393
2394 if unihash != self.rqdata.runtaskentries[tid].unihash:
Andrew Geisslerc926e172021-05-07 16:11:35 -05002395 # Make sure we rehash any other tasks with the same task hash that we're deferred against.
2396 torehash = [tid]
2397 for deftid in self.sq_deferred:
2398 if self.sq_deferred[deftid] == tid:
2399 torehash.append(deftid)
2400 for hashtid in torehash:
2401 hashequiv_logger.verbose("Task %s unihash changed to %s" % (hashtid, unihash))
2402 self.rqdata.runtaskentries[hashtid].unihash = unihash
2403 bb.parse.siggen.set_unihash(hashtid, unihash)
2404 toprocess.add(hashtid)
Andrew Geissler78b72792022-06-14 06:47:25 -05002405 if torehash:
2406 # Need to save after set_unihash above
2407 bb.parse.siggen.save_unitaskhashes()
Brad Bishop08902b02019-08-20 09:16:51 -04002408
Andrew Geissler82c905d2020-04-13 13:39:40 -05002409 # Work out all tasks which depend upon these
2410 total = set()
2411 next = set()
2412 for p in toprocess:
2413 next |= self.rqdata.runtaskentries[p].revdeps
2414 while next:
2415 current = next.copy()
2416 total = total | next
2417 next = set()
2418 for ntid in current:
2419 next |= self.rqdata.runtaskentries[ntid].revdeps
2420 next.difference_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002421
Andrew Geissler82c905d2020-04-13 13:39:40 -05002422 # Now iterate those tasks in dependency order to regenerate their taskhash/unihash
2423 next = set()
2424 for p in total:
Andrew Geissler595f6302022-01-24 19:11:47 +00002425 if not self.rqdata.runtaskentries[p].depends:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002426 next.add(p)
2427 elif self.rqdata.runtaskentries[p].depends.isdisjoint(total):
2428 next.add(p)
2429
2430 # When an item doesn't have dependencies in total, we can process it. Drop items from total when handled
2431 while next:
2432 current = next.copy()
2433 next = set()
2434 for tid in current:
Andrew Geissler595f6302022-01-24 19:11:47 +00002435 if self.rqdata.runtaskentries[p].depends and not self.rqdata.runtaskentries[tid].depends.isdisjoint(total):
Andrew Geissler82c905d2020-04-13 13:39:40 -05002436 continue
2437 orighash = self.rqdata.runtaskentries[tid].hash
Andrew Geissler517393d2023-01-13 08:55:19 -06002438 newhash = bb.parse.siggen.get_taskhash(tid, self.rqdata.runtaskentries[tid].depends, self.rqdata.dataCaches)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002439 origuni = self.rqdata.runtaskentries[tid].unihash
2440 newuni = bb.parse.siggen.get_unihash(tid)
2441 # FIXME, need to check it can come from sstate at all for determinism?
2442 remapped = False
2443 if newuni == origuni:
2444 # Nothing to do, we match, skip code below
2445 remapped = True
2446 elif tid in self.scenequeue_covered or tid in self.sq_live:
2447 # Already ran this setscene task or it running. Report the new taskhash
2448 bb.parse.siggen.report_unihash_equiv(tid, newhash, origuni, newuni, self.rqdata.dataCaches)
2449 hashequiv_logger.verbose("Already covered setscene for %s so ignoring rehash (remap)" % (tid))
2450 remapped = True
2451
2452 if not remapped:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002453 #logger.debug("Task %s hash changes: %s->%s %s->%s" % (tid, orighash, newhash, origuni, newuni))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002454 self.rqdata.runtaskentries[tid].hash = newhash
2455 self.rqdata.runtaskentries[tid].unihash = newuni
2456 changed.add(tid)
2457
2458 next |= self.rqdata.runtaskentries[tid].revdeps
2459 total.remove(tid)
2460 next.intersection_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002461
2462 if changed:
2463 for mc in self.rq.worker:
2464 self.rq.worker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2465 for mc in self.rq.fakeworker:
2466 self.rq.fakeworker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2467
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002468 hashequiv_logger.debug(pprint.pformat("Tasks changed:\n%s" % (changed)))
Brad Bishop08902b02019-08-20 09:16:51 -04002469
2470 for tid in changed:
2471 if tid not in self.rqdata.runq_setscene_tids:
2472 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002473 if tid not in self.pending_migrations:
2474 self.pending_migrations.add(tid)
2475
Andrew Geissler82c905d2020-04-13 13:39:40 -05002476 update_tasks = []
Brad Bishop08902b02019-08-20 09:16:51 -04002477 for tid in self.pending_migrations.copy():
Andrew Geissler82c905d2020-04-13 13:39:40 -05002478 if tid in self.runq_running or tid in self.sq_live:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002479 # Too late, task already running, not much we can do now
2480 self.pending_migrations.remove(tid)
2481 continue
2482
Brad Bishop08902b02019-08-20 09:16:51 -04002483 valid = True
2484 # Check no tasks this covers are running
2485 for dep in self.sqdata.sq_covered_tasks[tid]:
2486 if dep in self.runq_running and dep not in self.runq_complete:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002487 hashequiv_logger.debug2("Task %s is running which blocks setscene for %s from running" % (dep, tid))
Brad Bishop08902b02019-08-20 09:16:51 -04002488 valid = False
2489 break
2490 if not valid:
2491 continue
2492
2493 self.pending_migrations.remove(tid)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002494 changed = True
Brad Bishop08902b02019-08-20 09:16:51 -04002495
2496 if tid in self.tasks_scenequeue_done:
2497 self.tasks_scenequeue_done.remove(tid)
2498 for dep in self.sqdata.sq_covered_tasks[tid]:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002499 if dep in self.runq_complete and dep not in self.runq_tasksrun:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002500 bb.error("Task %s marked as completed but now needing to rerun? Halting build." % dep)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002501 self.failed_tids.append(tid)
2502 self.rq.state = runQueueCleanUp
2503 return
2504
Brad Bishop08902b02019-08-20 09:16:51 -04002505 if dep not in self.runq_complete:
2506 if dep in self.tasks_scenequeue_done and dep not in self.sqdata.unskippable:
2507 self.tasks_scenequeue_done.remove(dep)
2508
2509 if tid in self.sq_buildable:
2510 self.sq_buildable.remove(tid)
2511 if tid in self.sq_running:
2512 self.sq_running.remove(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002513 if tid in self.sqdata.outrightfail:
2514 self.sqdata.outrightfail.remove(tid)
2515 if tid in self.scenequeue_notcovered:
2516 self.scenequeue_notcovered.remove(tid)
2517 if tid in self.scenequeue_covered:
2518 self.scenequeue_covered.remove(tid)
2519 if tid in self.scenequeue_notneeded:
2520 self.scenequeue_notneeded.remove(tid)
2521
2522 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2523 self.sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2524
2525 if tid in self.stampcache:
2526 del self.stampcache[tid]
2527
2528 if tid in self.build_stamps:
2529 del self.build_stamps[tid]
2530
2531 update_tasks.append(tid)
2532
2533 update_tasks2 = []
2534 for tid in update_tasks:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002535 harddepfail = False
2536 for t in self.sqdata.sq_harddeps:
2537 if tid in self.sqdata.sq_harddeps[t] and t in self.scenequeue_notcovered:
2538 harddepfail = True
2539 break
2540 if not harddepfail and self.sqdata.sq_revdeps[tid].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
Brad Bishop08902b02019-08-20 09:16:51 -04002541 if tid not in self.sq_buildable:
2542 self.sq_buildable.add(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +00002543 if not self.sqdata.sq_revdeps[tid]:
Brad Bishop08902b02019-08-20 09:16:51 -04002544 self.sq_buildable.add(tid)
2545
Andrew Geissler517393d2023-01-13 08:55:19 -06002546 update_tasks2.append((tid, harddepfail, tid in self.sqdata.valid))
Brad Bishop08902b02019-08-20 09:16:51 -04002547
Andrew Geissler517393d2023-01-13 08:55:19 -06002548 if update_tasks2:
Brad Bishop08902b02019-08-20 09:16:51 -04002549 self.sqdone = False
Patrick Williams92b42cb2022-09-03 06:53:57 -05002550 for mc in sorted(self.sqdata.multiconfigs):
Andrew Geissler517393d2023-01-13 08:55:19 -06002551 for tid in sorted([t[0] for t in update_tasks2]):
Patrick Williams92b42cb2022-09-03 06:53:57 -05002552 if mc_from_tid(tid) != mc:
2553 continue
2554 h = pending_hash_index(tid, self.rqdata)
2555 if h in self.sqdata.hashes and tid != self.sqdata.hashes[h]:
2556 self.sq_deferred[tid] = self.sqdata.hashes[h]
2557 bb.note("Deferring %s after %s" % (tid, self.sqdata.hashes[h]))
Andrew Geissler517393d2023-01-13 08:55:19 -06002558 update_scenequeue_data([t[0] for t in update_tasks2], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002559
Andrew Geissler517393d2023-01-13 08:55:19 -06002560 for (tid, harddepfail, origvalid) in update_tasks2:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002561 if tid in self.sqdata.valid and not origvalid:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002562 hashequiv_logger.verbose("Setscene task %s became valid" % tid)
2563 if harddepfail:
Andrew Geissler517393d2023-01-13 08:55:19 -06002564 logger.debug2("%s has an unavailable hard dependency so skipping" % (tid))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002565 self.sq_task_failoutright(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002566
2567 if changed:
Andrew Geissler5199d832021-09-24 16:47:35 -05002568 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002569 self.holdoff_need_update = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002570
Brad Bishop96ff1982019-08-19 13:50:42 -04002571 def scenequeue_updatecounters(self, task, fail=False):
Brad Bishop08902b02019-08-20 09:16:51 -04002572
2573 for dep in sorted(self.sqdata.sq_deps[task]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002574 if fail and task in self.sqdata.sq_harddeps and dep in self.sqdata.sq_harddeps[task]:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002575 if dep in self.scenequeue_covered or dep in self.scenequeue_notcovered:
2576 # dependency could be already processed, e.g. noexec setscene task
2577 continue
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002578 noexec, stamppresent = check_setscene_stamps(dep, self.rqdata, self.rq, self.stampcache)
2579 if noexec or stamppresent:
2580 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002581 logger.debug2("%s was unavailable and is a hard dependency of %s so skipping" % (task, dep))
Brad Bishop96ff1982019-08-19 13:50:42 -04002582 self.sq_task_failoutright(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002583 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002584 if self.sqdata.sq_revdeps[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2585 if dep not in self.sq_buildable:
2586 self.sq_buildable.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002587
Brad Bishop96ff1982019-08-19 13:50:42 -04002588 next = set([task])
2589 while next:
2590 new = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002591 for t in sorted(next):
Brad Bishop96ff1982019-08-19 13:50:42 -04002592 self.tasks_scenequeue_done.add(t)
2593 # Look down the dependency chain for non-setscene things which this task depends on
2594 # and mark as 'done'
2595 for dep in self.rqdata.runtaskentries[t].depends:
2596 if dep in self.rqdata.runq_setscene_tids or dep in self.tasks_scenequeue_done:
2597 continue
2598 if self.rqdata.runtaskentries[dep].revdeps.issubset(self.tasks_scenequeue_done):
2599 new.add(dep)
Brad Bishop96ff1982019-08-19 13:50:42 -04002600 next = new
2601
Andrew Geissler5199d832021-09-24 16:47:35 -05002602 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002603 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002604
2605 def sq_task_completeoutright(self, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002606 """
2607 Mark a task as completed
2608 Look at the reverse dependencies and mark any task with
2609 completed dependencies as buildable
2610 """
2611
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002612 logger.debug('Found task %s which could be accelerated', task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002613 self.scenequeue_covered.add(task)
2614 self.scenequeue_updatecounters(task)
2615
Brad Bishop96ff1982019-08-19 13:50:42 -04002616 def sq_check_taskfail(self, task):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002617 if self.rqdata.setscene_ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002618 realtask = task.split('_setscene')[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002619 (mc, fn, taskname, taskfn) = split_tid_mcfn(realtask)
2620 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002621 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002622 logger.error('Task %s.%s failed' % (pn, taskname + "_setscene"))
2623 self.rq.state = runQueueCleanUp
2624
Brad Bishop96ff1982019-08-19 13:50:42 -04002625 def sq_task_complete(self, task):
Andrew Geissler5199d832021-09-24 16:47:35 -05002626 bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
Brad Bishop96ff1982019-08-19 13:50:42 -04002627 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002628
Brad Bishop96ff1982019-08-19 13:50:42 -04002629 def sq_task_fail(self, task, result):
Andrew Geissler5199d832021-09-24 16:47:35 -05002630 bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002631 self.scenequeue_notcovered.add(task)
2632 self.scenequeue_updatecounters(task, True)
Brad Bishop96ff1982019-08-19 13:50:42 -04002633 self.sq_check_taskfail(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002634
Brad Bishop96ff1982019-08-19 13:50:42 -04002635 def sq_task_failoutright(self, task):
2636 self.sq_running.add(task)
2637 self.sq_buildable.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002638 self.scenequeue_notcovered.add(task)
2639 self.scenequeue_updatecounters(task, True)
2640
Brad Bishop96ff1982019-08-19 13:50:42 -04002641 def sq_task_skip(self, task):
2642 self.sq_running.add(task)
2643 self.sq_buildable.add(task)
2644 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002645
Brad Bishop96ff1982019-08-19 13:50:42 -04002646 def sq_build_taskdepdata(self, task):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002647 def getsetscenedeps(tid):
2648 deps = set()
2649 (mc, fn, taskname, _) = split_tid_mcfn(tid)
2650 realtid = tid + "_setscene"
2651 idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
2652 for (depname, idependtask) in idepends:
2653 if depname not in self.rqdata.taskData[mc].build_targets:
2654 continue
2655
2656 depfn = self.rqdata.taskData[mc].build_targets[depname][0]
2657 if depfn is None:
2658 continue
2659 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2660 deps.add(deptid)
2661 return deps
2662
2663 taskdepdata = {}
2664 next = getsetscenedeps(task)
2665 next.add(task)
2666 while next:
2667 additional = []
2668 for revdep in next:
2669 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2670 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2671 deps = getsetscenedeps(revdep)
2672 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2673 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002674 unihash = self.rqdata.runtaskentries[revdep].unihash
2675 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002676 for revdep2 in deps:
2677 if revdep2 not in taskdepdata:
2678 additional.append(revdep2)
2679 next = additional
2680
2681 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2682 return taskdepdata
2683
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002684 def check_setscene_ignore_tasks(self, tid):
2685 # Check task that is going to run against the ignore tasks list
Brad Bishop96ff1982019-08-19 13:50:42 -04002686 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2687 # Ignore covered tasks
2688 if tid in self.tasks_covered:
2689 return False
2690 # Ignore stamped tasks
2691 if self.rq.check_stamp_task(tid, taskname, cache=self.stampcache):
2692 return False
2693 # Ignore noexec tasks
2694 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2695 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2696 return False
2697
2698 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002699 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Brad Bishop96ff1982019-08-19 13:50:42 -04002700 if tid in self.rqdata.runq_setscene_tids:
Andrew Geissler595f6302022-01-24 19:11:47 +00002701 msg = ['Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)]
Brad Bishop96ff1982019-08-19 13:50:42 -04002702 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002703 msg = ['Task %s.%s attempted to execute unexpectedly' % (pn, taskname)]
Andrew Geissler82c905d2020-04-13 13:39:40 -05002704 for t in self.scenequeue_notcovered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002705 msg.append("\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash))
2706 msg.append('\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered))
2707 logger.error("".join(msg))
Brad Bishop96ff1982019-08-19 13:50:42 -04002708 return True
2709 return False
2710
2711class SQData(object):
2712 def __init__(self):
2713 # SceneQueue dependencies
2714 self.sq_deps = {}
2715 # SceneQueue reverse dependencies
2716 self.sq_revdeps = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002717 # Injected inter-setscene task dependencies
2718 self.sq_harddeps = {}
2719 # Cache of stamp files so duplicates can't run in parallel
2720 self.stamps = {}
2721 # Setscene tasks directly depended upon by the build
2722 self.unskippable = set()
2723 # List of setscene tasks which aren't present
2724 self.outrightfail = set()
2725 # A list of normal tasks a setscene task covers
2726 self.sq_covered_tasks = {}
2727
2728def build_scenequeue_data(sqdata, rqdata, rq, cooker, stampcache, sqrq):
2729
2730 sq_revdeps = {}
2731 sq_revdeps_squash = {}
2732 sq_collated_deps = {}
2733
2734 # We need to construct a dependency graph for the setscene functions. Intermediate
2735 # dependencies between the setscene tasks only complicate the code. This code
2736 # therefore aims to collapse the huge runqueue dependency tree into a smaller one
2737 # only containing the setscene functions.
2738
2739 rqdata.init_progress_reporter.next_stage()
2740
2741 # First process the chains up to the first setscene task.
2742 endpoints = {}
2743 for tid in rqdata.runtaskentries:
2744 sq_revdeps[tid] = copy.copy(rqdata.runtaskentries[tid].revdeps)
2745 sq_revdeps_squash[tid] = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00002746 if not sq_revdeps[tid] and tid not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002747 #bb.warn("Added endpoint %s" % (tid))
2748 endpoints[tid] = set()
2749
2750 rqdata.init_progress_reporter.next_stage()
2751
2752 # Secondly process the chains between setscene tasks.
2753 for tid in rqdata.runq_setscene_tids:
2754 sq_collated_deps[tid] = set()
2755 #bb.warn("Added endpoint 2 %s" % (tid))
2756 for dep in rqdata.runtaskentries[tid].depends:
2757 if tid in sq_revdeps[dep]:
2758 sq_revdeps[dep].remove(tid)
2759 if dep not in endpoints:
2760 endpoints[dep] = set()
2761 #bb.warn(" Added endpoint 3 %s" % (dep))
2762 endpoints[dep].add(tid)
2763
2764 rqdata.init_progress_reporter.next_stage()
2765
2766 def process_endpoints(endpoints):
2767 newendpoints = {}
2768 for point, task in endpoints.items():
2769 tasks = set()
2770 if task:
2771 tasks |= task
2772 if sq_revdeps_squash[point]:
2773 tasks |= sq_revdeps_squash[point]
2774 if point not in rqdata.runq_setscene_tids:
2775 for t in tasks:
2776 sq_collated_deps[t].add(point)
2777 sq_revdeps_squash[point] = set()
2778 if point in rqdata.runq_setscene_tids:
2779 sq_revdeps_squash[point] = tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002780 continue
2781 for dep in rqdata.runtaskentries[point].depends:
2782 if point in sq_revdeps[dep]:
2783 sq_revdeps[dep].remove(point)
2784 if tasks:
2785 sq_revdeps_squash[dep] |= tasks
Andrew Geissler595f6302022-01-24 19:11:47 +00002786 if not sq_revdeps[dep] and dep not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002787 newendpoints[dep] = task
Andrew Geissler595f6302022-01-24 19:11:47 +00002788 if newendpoints:
Brad Bishop96ff1982019-08-19 13:50:42 -04002789 process_endpoints(newendpoints)
2790
2791 process_endpoints(endpoints)
2792
2793 rqdata.init_progress_reporter.next_stage()
2794
Brad Bishop08902b02019-08-20 09:16:51 -04002795 # Build a list of tasks which are "unskippable"
2796 # These are direct endpoints referenced by the build upto and including setscene tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002797 # Take the build endpoints (no revdeps) and find the sstate tasks they depend upon
2798 new = True
2799 for tid in rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00002800 if not rqdata.runtaskentries[tid].revdeps:
Brad Bishop96ff1982019-08-19 13:50:42 -04002801 sqdata.unskippable.add(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002802 sqdata.unskippable |= sqrq.cantskip
Brad Bishop96ff1982019-08-19 13:50:42 -04002803 while new:
2804 new = False
Brad Bishop08902b02019-08-20 09:16:51 -04002805 orig = sqdata.unskippable.copy()
2806 for tid in sorted(orig, reverse=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04002807 if tid in rqdata.runq_setscene_tids:
2808 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00002809 if not rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04002810 # These are tasks which have no setscene tasks in their chain, need to mark as directly buildable
Brad Bishop96ff1982019-08-19 13:50:42 -04002811 sqrq.setbuildable(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002812 sqdata.unskippable |= rqdata.runtaskentries[tid].depends
Brad Bishop08902b02019-08-20 09:16:51 -04002813 if sqdata.unskippable != orig:
2814 new = True
2815
2816 sqrq.tasks_scenequeue_done |= sqdata.unskippable.difference(rqdata.runq_setscene_tids)
Brad Bishop96ff1982019-08-19 13:50:42 -04002817
2818 rqdata.init_progress_reporter.next_stage(len(rqdata.runtaskentries))
2819
2820 # Sanity check all dependencies could be changed to setscene task references
2821 for taskcounter, tid in enumerate(rqdata.runtaskentries):
2822 if tid in rqdata.runq_setscene_tids:
2823 pass
Andrew Geissler595f6302022-01-24 19:11:47 +00002824 elif sq_revdeps_squash[tid]:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002825 bb.msg.fatal("RunQueue", "Something went badly wrong during scenequeue generation, halting. Please report this problem.")
Brad Bishop96ff1982019-08-19 13:50:42 -04002826 else:
2827 del sq_revdeps_squash[tid]
2828 rqdata.init_progress_reporter.update(taskcounter)
2829
2830 rqdata.init_progress_reporter.next_stage()
2831
2832 # Resolve setscene inter-task dependencies
2833 # e.g. do_sometask_setscene[depends] = "targetname:do_someothertask_setscene"
2834 # Note that anything explicitly depended upon will have its reverse dependencies removed to avoid circular dependencies
2835 for tid in rqdata.runq_setscene_tids:
2836 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2837 realtid = tid + "_setscene"
2838 idepends = rqdata.taskData[mc].taskentries[realtid].idepends
Andrew Geissler517393d2023-01-13 08:55:19 -06002839 sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2840
Brad Bishop96ff1982019-08-19 13:50:42 -04002841 for (depname, idependtask) in idepends:
2842
2843 if depname not in rqdata.taskData[mc].build_targets:
2844 continue
2845
2846 depfn = rqdata.taskData[mc].build_targets[depname][0]
2847 if depfn is None:
2848 continue
2849 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2850 if deptid not in rqdata.runtaskentries:
2851 bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (realtid, depfn, idependtask))
2852
2853 if not deptid in sqdata.sq_harddeps:
2854 sqdata.sq_harddeps[deptid] = set()
2855 sqdata.sq_harddeps[deptid].add(tid)
2856
2857 sq_revdeps_squash[tid].add(deptid)
2858 # Have to zero this to avoid circular dependencies
2859 sq_revdeps_squash[deptid] = set()
2860
2861 rqdata.init_progress_reporter.next_stage()
2862
2863 for task in sqdata.sq_harddeps:
2864 for dep in sqdata.sq_harddeps[task]:
2865 sq_revdeps_squash[dep].add(task)
2866
2867 rqdata.init_progress_reporter.next_stage()
2868
2869 #for tid in sq_revdeps_squash:
2870 # data = ""
2871 # for dep in sq_revdeps_squash[tid]:
2872 # data = data + "\n %s" % dep
2873 # bb.warn("Task %s_setscene: is %s " % (tid, data))
2874
2875 sqdata.sq_revdeps = sq_revdeps_squash
Brad Bishop96ff1982019-08-19 13:50:42 -04002876 sqdata.sq_covered_tasks = sq_collated_deps
2877
2878 # Build reverse version of revdeps to populate deps structure
2879 for tid in sqdata.sq_revdeps:
2880 sqdata.sq_deps[tid] = set()
2881 for tid in sqdata.sq_revdeps:
2882 for dep in sqdata.sq_revdeps[tid]:
2883 sqdata.sq_deps[dep].add(tid)
2884
2885 rqdata.init_progress_reporter.next_stage()
2886
Brad Bishop00e122a2019-10-05 11:10:57 -04002887 sqdata.multiconfigs = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002888 for tid in sqdata.sq_revdeps:
Brad Bishop00e122a2019-10-05 11:10:57 -04002889 sqdata.multiconfigs.add(mc_from_tid(tid))
Andrew Geissler595f6302022-01-24 19:11:47 +00002890 if not sqdata.sq_revdeps[tid]:
Brad Bishop96ff1982019-08-19 13:50:42 -04002891 sqrq.sq_buildable.add(tid)
2892
2893 rqdata.init_progress_reporter.finish()
2894
Brad Bishop00e122a2019-10-05 11:10:57 -04002895 sqdata.noexec = set()
2896 sqdata.stamppresent = set()
2897 sqdata.valid = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002898
Patrick Williams213cb262021-08-07 19:21:33 -05002899 sqdata.hashes = {}
2900 sqrq.sq_deferred = {}
2901 for mc in sorted(sqdata.multiconfigs):
2902 for tid in sorted(sqdata.sq_revdeps):
2903 if mc_from_tid(tid) != mc:
2904 continue
2905 h = pending_hash_index(tid, rqdata)
2906 if h not in sqdata.hashes:
2907 sqdata.hashes[h] = tid
2908 else:
2909 sqrq.sq_deferred[tid] = sqdata.hashes[h]
2910 bb.note("Deferring %s after %s" % (tid, sqdata.hashes[h]))
2911
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002912 update_scenequeue_data(sqdata.sq_revdeps, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002913
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002914 # Compute a list of 'stale' sstate tasks where the current hash does not match the one
2915 # in any stamp files. Pass the list out to metadata as an event.
2916 found = {}
2917 for tid in rqdata.runq_setscene_tids:
2918 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002919 stamps = bb.build.find_stale_stamps(taskname, taskfn)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002920 if stamps:
2921 if mc not in found:
2922 found[mc] = {}
2923 found[mc][tid] = stamps
2924 for mc in found:
2925 event = bb.event.StaleSetSceneTasks(found[mc])
2926 bb.event.fire(event, cooker.databuilder.mcdata[mc])
2927
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002928def check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=False):
2929
2930 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2931
2932 taskdep = rqdata.dataCaches[mc].task_deps[taskfn]
2933
2934 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Andrew Geissler517393d2023-01-13 08:55:19 -06002935 bb.build.make_stamp_mcfn(taskname + "_setscene", taskfn)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002936 return True, False
2937
2938 if rq.check_stamp_task(tid, taskname + "_setscene", cache=stampcache):
2939 logger.debug2('Setscene stamp current for task %s', tid)
2940 return False, True
2941
2942 if rq.check_stamp_task(tid, taskname, recurse = True, cache=stampcache):
2943 logger.debug2('Normal stamp current for task %s', tid)
2944 return False, True
2945
2946 return False, False
2947
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002948def update_scenequeue_data(tids, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True):
Brad Bishop00e122a2019-10-05 11:10:57 -04002949
2950 tocheck = set()
2951
2952 for tid in sorted(tids):
2953 if tid in sqdata.stamppresent:
2954 sqdata.stamppresent.remove(tid)
2955 if tid in sqdata.valid:
2956 sqdata.valid.remove(tid)
Andrew Geisslerc926e172021-05-07 16:11:35 -05002957 if tid in sqdata.outrightfail:
2958 sqdata.outrightfail.remove(tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04002959
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002960 noexec, stamppresent = check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002961
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002962 if noexec:
Brad Bishop00e122a2019-10-05 11:10:57 -04002963 sqdata.noexec.add(tid)
2964 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002965 logger.debug2("%s is noexec so skipping setscene" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04002966 continue
2967
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002968 if stamppresent:
Brad Bishop00e122a2019-10-05 11:10:57 -04002969 sqdata.stamppresent.add(tid)
2970 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002971 logger.debug2("%s has a valid stamp, skipping" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04002972 continue
2973
2974 tocheck.add(tid)
2975
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002976 sqdata.valid |= rq.validate_hashes(tocheck, cooker.data, len(sqdata.stamppresent), False, summary=summary)
Brad Bishop00e122a2019-10-05 11:10:57 -04002977
Patrick Williams213cb262021-08-07 19:21:33 -05002978 for tid in tids:
2979 if tid in sqdata.stamppresent:
2980 continue
2981 if tid in sqdata.valid:
2982 continue
2983 if tid in sqdata.noexec:
2984 continue
2985 if tid in sqrq.scenequeue_covered:
2986 continue
2987 if tid in sqrq.scenequeue_notcovered:
2988 continue
2989 if tid in sqrq.sq_deferred:
2990 continue
2991 sqdata.outrightfail.add(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002992 logger.debug2("%s already handled (fallthrough), skipping" % (tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04002993
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002994class TaskFailure(Exception):
2995 """
2996 Exception raised when a task in a runqueue fails
2997 """
2998 def __init__(self, x):
2999 self.args = x
3000
3001
3002class runQueueExitWait(bb.event.Event):
3003 """
3004 Event when waiting for task processes to exit
3005 """
3006
3007 def __init__(self, remain):
3008 self.remain = remain
3009 self.message = "Waiting for %s active tasks to finish" % remain
3010 bb.event.Event.__init__(self)
3011
3012class runQueueEvent(bb.event.Event):
3013 """
3014 Base runQueue event class
3015 """
3016 def __init__(self, task, stats, rq):
3017 self.taskid = task
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003018 self.taskstring = task
3019 self.taskname = taskname_from_tid(task)
3020 self.taskfile = fn_from_tid(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003021 self.taskhash = rq.rqdata.get_task_hash(task)
3022 self.stats = stats.copy()
3023 bb.event.Event.__init__(self)
3024
3025class sceneQueueEvent(runQueueEvent):
3026 """
3027 Base sceneQueue event class
3028 """
3029 def __init__(self, task, stats, rq, noexec=False):
3030 runQueueEvent.__init__(self, task, stats, rq)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003031 self.taskstring = task + "_setscene"
3032 self.taskname = taskname_from_tid(task) + "_setscene"
3033 self.taskfile = fn_from_tid(task)
3034 self.taskhash = rq.rqdata.get_task_hash(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003035
3036class runQueueTaskStarted(runQueueEvent):
3037 """
3038 Event notifying a task was started
3039 """
3040 def __init__(self, task, stats, rq, noexec=False):
3041 runQueueEvent.__init__(self, task, stats, rq)
3042 self.noexec = noexec
3043
3044class sceneQueueTaskStarted(sceneQueueEvent):
3045 """
3046 Event notifying a setscene task was started
3047 """
3048 def __init__(self, task, stats, rq, noexec=False):
3049 sceneQueueEvent.__init__(self, task, stats, rq)
3050 self.noexec = noexec
3051
3052class runQueueTaskFailed(runQueueEvent):
3053 """
3054 Event notifying a task failed
3055 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003056 def __init__(self, task, stats, exitcode, rq, fakeroot_log=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003057 runQueueEvent.__init__(self, task, stats, rq)
3058 self.exitcode = exitcode
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003059 self.fakeroot_log = fakeroot_log
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003060
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003061 def __str__(self):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003062 if self.fakeroot_log:
3063 return "Task (%s) failed with exit code '%s' \nPseudo log:\n%s" % (self.taskstring, self.exitcode, self.fakeroot_log)
3064 else:
3065 return "Task (%s) failed with exit code '%s'" % (self.taskstring, self.exitcode)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003066
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003067class sceneQueueTaskFailed(sceneQueueEvent):
3068 """
3069 Event notifying a setscene task failed
3070 """
3071 def __init__(self, task, stats, exitcode, rq):
3072 sceneQueueEvent.__init__(self, task, stats, rq)
3073 self.exitcode = exitcode
3074
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003075 def __str__(self):
3076 return "Setscene task (%s) failed with exit code '%s' - real task will be run instead" % (self.taskstring, self.exitcode)
3077
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003078class sceneQueueComplete(sceneQueueEvent):
3079 """
3080 Event when all the sceneQueue tasks are complete
3081 """
3082 def __init__(self, stats, rq):
3083 self.stats = stats.copy()
3084 bb.event.Event.__init__(self)
3085
3086class runQueueTaskCompleted(runQueueEvent):
3087 """
3088 Event notifying a task completed
3089 """
3090
3091class sceneQueueTaskCompleted(sceneQueueEvent):
3092 """
3093 Event notifying a setscene task completed
3094 """
3095
3096class runQueueTaskSkipped(runQueueEvent):
3097 """
3098 Event notifying a task was skipped
3099 """
3100 def __init__(self, task, stats, rq, reason):
3101 runQueueEvent.__init__(self, task, stats, rq)
3102 self.reason = reason
3103
Brad Bishop08902b02019-08-20 09:16:51 -04003104class taskUniHashUpdate(bb.event.Event):
3105 """
3106 Base runQueue event class
3107 """
3108 def __init__(self, task, unihash):
3109 self.taskid = task
3110 self.unihash = unihash
3111 bb.event.Event.__init__(self)
3112
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003113class runQueuePipe():
3114 """
3115 Abstraction for a pipe between a worker thread and the server
3116 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003117 def __init__(self, pipein, pipeout, d, rq, rqexec, fakerootlogs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003118 self.input = pipein
3119 if pipeout:
3120 pipeout.close()
3121 bb.utils.nonblockingfd(self.input)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003122 self.queue = b""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003123 self.d = d
3124 self.rq = rq
3125 self.rqexec = rqexec
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003126 self.fakerootlogs = fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003127
3128 def setrunqueueexec(self, rqexec):
3129 self.rqexec = rqexec
3130
3131 def read(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003132 for workers, name in [(self.rq.worker, "Worker"), (self.rq.fakeworker, "Fakeroot")]:
3133 for worker in workers.values():
3134 worker.process.poll()
3135 if worker.process.returncode is not None and not self.rq.teardown:
3136 bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, worker.process.pid, str(worker.process.returncode)))
3137 self.rq.finish_runqueue(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003138
3139 start = len(self.queue)
3140 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003141 self.queue = self.queue + (self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003142 except (OSError, IOError) as e:
3143 if e.errno != errno.EAGAIN:
3144 raise
3145 end = len(self.queue)
3146 found = True
Andrew Geissler595f6302022-01-24 19:11:47 +00003147 while found and self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003148 found = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003149 index = self.queue.find(b"</event>")
3150 while index != -1 and self.queue.startswith(b"<event>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003151 try:
3152 event = pickle.loads(self.queue[7:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003153 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
3154 if isinstance(e, pickle.UnpicklingError) and "truncated" in str(e):
3155 # The pickled data could contain "</event>" so search for the next occurance
3156 # unpickling again, this should be the only way an unpickle error could occur
3157 index = self.queue.find(b"</event>", index + 1)
3158 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003159 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
3160 bb.event.fire_from_worker(event, self.d)
Brad Bishop08902b02019-08-20 09:16:51 -04003161 if isinstance(event, taskUniHashUpdate):
3162 self.rqexec.updated_taskhash_queue.append((event.taskid, event.unihash))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003163 found = True
3164 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003165 index = self.queue.find(b"</event>")
3166 index = self.queue.find(b"</exitcode>")
3167 while index != -1 and self.queue.startswith(b"<exitcode>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003168 try:
3169 task, status = pickle.loads(self.queue[10:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003170 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003171 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003172 (_, _, _, taskfn) = split_tid_mcfn(task)
3173 fakerootlog = None
3174 if self.fakerootlogs and taskfn and taskfn in self.fakerootlogs:
3175 fakerootlog = self.fakerootlogs[taskfn]
3176 self.rqexec.runqueue_process_waitpid(task, status, fakerootlog=fakerootlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003177 found = True
3178 self.queue = self.queue[index+11:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003179 index = self.queue.find(b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003180 return (end > start)
3181
3182 def close(self):
3183 while self.read():
3184 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00003185 if self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003186 print("Warning, worker left partial message: %s" % self.queue)
3187 self.input.close()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003188
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003189def get_setscene_enforce_ignore_tasks(d, targets):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003190 if d.getVar('BB_SETSCENE_ENFORCE') != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003191 return None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003192 ignore_tasks = (d.getVar("BB_SETSCENE_ENFORCE_IGNORE_TASKS") or "").split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003193 outlist = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003194 for item in ignore_tasks[:]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003195 if item.startswith('%:'):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003196 for (mc, target, task, fn) in targets:
3197 outlist.append(target + ':' + item.split(':')[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003198 else:
3199 outlist.append(item)
3200 return outlist
3201
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003202def check_setscene_enforce_ignore_tasks(pn, taskname, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003203 import fnmatch
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003204 if ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003205 item = '%s:%s' % (pn, taskname)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003206 for ignore_tasks in ignore_tasks:
3207 if fnmatch.fnmatch(item, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003208 return True
3209 return False
3210 return True