blob: 48788f4aa659e50f23938c1440e1ae44a2c3250d [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 now = time.time()
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500202 tdiff = now - self.prev_pressure_time
203 if tdiff > 1.0:
204 exceeds_cpu_pressure = self.rq.max_cpu_pressure and (float(curr_cpu_pressure) - float(self.prev_cpu_pressure)) / tdiff > self.rq.max_cpu_pressure
205 exceeds_io_pressure = self.rq.max_io_pressure and (float(curr_io_pressure) - float(self.prev_io_pressure)) / tdiff > self.rq.max_io_pressure
206 exceeds_memory_pressure = self.rq.max_memory_pressure and (float(curr_memory_pressure) - float(self.prev_memory_pressure)) / tdiff > self.rq.max_memory_pressure
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500207 self.prev_cpu_pressure = curr_cpu_pressure
208 self.prev_io_pressure = curr_io_pressure
Patrick Williams92b42cb2022-09-03 06:53:57 -0500209 self.prev_memory_pressure = curr_memory_pressure
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500210 self.prev_pressure_time = now
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500211 else:
212 exceeds_cpu_pressure = self.rq.max_cpu_pressure and (float(curr_cpu_pressure) - float(self.prev_cpu_pressure)) > self.rq.max_cpu_pressure
213 exceeds_io_pressure = self.rq.max_io_pressure and (float(curr_io_pressure) - float(self.prev_io_pressure)) > self.rq.max_io_pressure
214 exceeds_memory_pressure = self.rq.max_memory_pressure and (float(curr_memory_pressure) - float(self.prev_memory_pressure)) > self.rq.max_memory_pressure
Andrew Geissler8f840682023-07-21 09:09:43 -0500215 pressure_state = (exceeds_cpu_pressure, exceeds_io_pressure, exceeds_memory_pressure)
216 if hasattr(self, "pressure_state") and pressure_state != self.pressure_state:
217 bb.note("Pressure status changed to CPU: %s, IO: %s, Mem: %s" % pressure_state)
218 self.pressure_state = pressure_state
Patrick Williams92b42cb2022-09-03 06:53:57 -0500219 return (exceeds_cpu_pressure or exceeds_io_pressure or exceeds_memory_pressure)
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500220 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221
222 def next_buildable_task(self):
223 """
224 Return the id of the first task we find that is buildable
225 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500226 # Once tasks are running we don't need to worry about them again
227 self.buildable.difference_update(self.rq.runq_running)
Brad Bishop08902b02019-08-20 09:16:51 -0400228 buildable = set(self.buildable)
Brad Bishop08902b02019-08-20 09:16:51 -0400229 buildable.difference_update(self.rq.holdoff_tasks)
230 buildable.intersection_update(self.rq.tasks_covered | self.rq.tasks_notcovered)
Brad Bishop96ff1982019-08-19 13:50:42 -0400231 if not buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500232 return None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800233
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500234 # Bitbake requires that at least one task be active. Only check for pressure if
235 # this is the case, otherwise the pressure limitation could result in no tasks
236 # being active and no new tasks started thereby, at times, breaking the scheduler.
237 if self.rq.stats.active and self.exceeds_max_pressure():
238 return None
239
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800240 # Filter out tasks that have a max number of threads that have been exceeded
241 skip_buildable = {}
242 for running in self.rq.runq_running.difference(self.rq.runq_complete):
243 rtaskname = taskname_from_tid(running)
244 if rtaskname not in self.skip_maxthread:
245 self.skip_maxthread[rtaskname] = self.rq.cfgData.getVarFlag(rtaskname, "number_threads")
246 if not self.skip_maxthread[rtaskname]:
247 continue
248 if rtaskname in skip_buildable:
249 skip_buildable[rtaskname] += 1
250 else:
251 skip_buildable[rtaskname] = 1
252
Brad Bishop96ff1982019-08-19 13:50:42 -0400253 if len(buildable) == 1:
Brad Bishop08902b02019-08-20 09:16:51 -0400254 tid = buildable.pop()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800255 taskname = taskname_from_tid(tid)
256 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
257 return None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600258 stamp = self.stamps[tid]
259 if stamp not in self.rq.build_stamps.values():
260 return tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500261
262 if not self.rev_prio_map:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600263 self.rev_prio_map = {}
264 for tid in self.rqdata.runtaskentries:
265 self.rev_prio_map[tid] = self.prio_map.index(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500266
267 best = None
268 bestprio = None
Brad Bishop96ff1982019-08-19 13:50:42 -0400269 for tid in buildable:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800270 taskname = taskname_from_tid(tid)
271 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
272 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600273 prio = self.rev_prio_map[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274 if bestprio is None or bestprio > prio:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600275 stamp = self.stamps[tid]
276 if stamp in self.rq.build_stamps.values():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500277 continue
278 bestprio = prio
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600279 best = tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500280
281 return best
282
283 def next(self):
284 """
285 Return the id of the task we should build next
286 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800287 if self.rq.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500288 return self.next_buildable_task()
289
Brad Bishop316dfdd2018-06-25 12:45:53 -0400290 def newbuildable(self, task):
Brad Bishop08902b02019-08-20 09:16:51 -0400291 self.buildable.add(task)
292
293 def removebuildable(self, task):
294 self.buildable.remove(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500295
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500296 def describe_task(self, taskid):
297 result = 'ID %s' % taskid
298 if self.rev_prio_map:
299 result = result + (' pri %d' % self.rev_prio_map[taskid])
300 return result
301
302 def dump_prio(self, comment):
303 bb.debug(3, '%s (most important first):\n%s' %
304 (comment,
305 '\n'.join(['%d. %s' % (index + 1, self.describe_task(taskid)) for
306 index, taskid in enumerate(self.prio_map)])))
307
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500308class RunQueueSchedulerSpeed(RunQueueScheduler):
309 """
310 A scheduler optimised for speed. The priority map is sorted by task weight,
311 heavier weighted tasks (tasks needed by the most other tasks) are run first.
312 """
313 name = "speed"
314
315 def __init__(self, runqueue, rqdata):
316 """
317 The priority map is sorted by task weight.
318 """
319 RunQueueScheduler.__init__(self, runqueue, rqdata)
320
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600321 weights = {}
322 for tid in self.rqdata.runtaskentries:
323 weight = self.rqdata.runtaskentries[tid].weight
324 if not weight in weights:
325 weights[weight] = []
326 weights[weight].append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500327
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600328 self.prio_map = []
329 for weight in sorted(weights):
330 for w in weights[weight]:
331 self.prio_map.append(w)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500332
333 self.prio_map.reverse()
334
335class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed):
336 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500337 A scheduler optimised to complete .bb files as quickly as possible. The
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500338 priority map is sorted by task weight, but then reordered so once a given
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500339 .bb file starts to build, it's completed as quickly as possible by
340 running all tasks related to the same .bb file one after the after.
341 This works well where disk space is at a premium and classes like OE's
342 rm_work are in force.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500343 """
344 name = "completion"
345
346 def __init__(self, runqueue, rqdata):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500347 super(RunQueueSchedulerCompletion, self).__init__(runqueue, rqdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500348
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500349 # Extract list of tasks for each recipe, with tasks sorted
350 # ascending from "must run first" (typically do_fetch) to
351 # "runs last" (do_build). The speed scheduler prioritizes
352 # tasks that must run first before the ones that run later;
353 # this is what we depend on here.
354 task_lists = {}
355 for taskid in self.prio_map:
356 fn, taskname = taskid.rsplit(':', 1)
357 task_lists.setdefault(fn, []).append(taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500358
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500359 # Now unify the different task lists. The strategy is that
360 # common tasks get skipped and new ones get inserted after the
361 # preceeding common one(s) as they are found. Because task
362 # lists should differ only by their number of tasks, but not
363 # the ordering of the common tasks, this should result in a
364 # deterministic result that is a superset of the individual
365 # task ordering.
366 all_tasks = []
367 for recipe, new_tasks in task_lists.items():
368 index = 0
369 old_task = all_tasks[index] if index < len(all_tasks) else None
370 for new_task in new_tasks:
371 if old_task == new_task:
372 # Common task, skip it. This is the fast-path which
373 # avoids a full search.
374 index += 1
375 old_task = all_tasks[index] if index < len(all_tasks) else None
376 else:
377 try:
378 index = all_tasks.index(new_task)
379 # Already present, just not at the current
380 # place. We re-synchronized by changing the
381 # index so that it matches again. Now
382 # move on to the next existing task.
383 index += 1
384 old_task = all_tasks[index] if index < len(all_tasks) else None
385 except ValueError:
386 # Not present. Insert before old_task, which
387 # remains the same (but gets shifted back).
388 all_tasks.insert(index, new_task)
389 index += 1
390 bb.debug(3, 'merged task list: %s' % all_tasks)
391
392 # Now reverse the order so that tasks that finish the work on one
393 # recipe are considered more imporant (= come first). The ordering
394 # is now so that do_build is most important.
395 all_tasks.reverse()
396
397 # Group tasks of the same kind before tasks of less important
398 # kinds at the head of the queue (because earlier = lower
399 # priority number = runs earlier), while preserving the
400 # ordering by recipe. If recipe foo is more important than
401 # bar, then the goal is to work on foo's do_populate_sysroot
402 # before bar's do_populate_sysroot and on the more important
403 # tasks of foo before any of the less important tasks in any
404 # other recipe (if those other recipes are more important than
405 # foo).
406 #
407 # All of this only applies when tasks are runable. Explicit
408 # dependencies still override this ordering by priority.
409 #
410 # Here's an example why this priority re-ordering helps with
411 # minimizing disk usage. Consider a recipe foo with a higher
412 # priority than bar where foo DEPENDS on bar. Then the
413 # implicit rule (from base.bbclass) is that foo's do_configure
414 # depends on bar's do_populate_sysroot. This ensures that
415 # bar's do_populate_sysroot gets done first. Normally the
416 # tasks from foo would continue to run once that is done, and
417 # bar only gets completed and cleaned up later. By ordering
418 # bar's task that depend on bar's do_populate_sysroot before foo's
419 # do_configure, that problem gets avoided.
420 task_index = 0
421 self.dump_prio('original priorities')
422 for task in all_tasks:
423 for index in range(task_index, self.numTasks):
424 taskid = self.prio_map[index]
425 taskname = taskid.rsplit(':', 1)[1]
426 if taskname == task:
427 del self.prio_map[index]
428 self.prio_map.insert(task_index, taskid)
429 task_index += 1
430 self.dump_prio('completion priorities')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500431
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600432class RunTaskEntry(object):
433 def __init__(self):
434 self.depends = set()
435 self.revdeps = set()
436 self.hash = None
Brad Bishop19323692019-04-05 15:28:33 -0400437 self.unihash = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600438 self.task = None
439 self.weight = 1
440
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500441class RunQueueData:
442 """
443 BitBake Run Queue implementation
444 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600445 def __init__(self, rq, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446 self.cooker = cooker
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600447 self.dataCaches = dataCaches
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500448 self.taskData = taskData
449 self.targets = targets
450 self.rq = rq
451 self.warn_multi_bb = False
452
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000453 self.multi_provider_allowed = (cfgData.getVar("BB_MULTI_PROVIDER_ALLOWED") or "").split()
454 self.setscene_ignore_tasks = get_setscene_enforce_ignore_tasks(cfgData, targets)
455 self.setscene_ignore_tasks_checked = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500456 self.setscene_enforce = (cfgData.getVar('BB_SETSCENE_ENFORCE') == "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600457 self.init_progress_reporter = bb.progress.DummyMultiStageProcessProgressReporter()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500458
459 self.reset()
460
461 def reset(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600462 self.runtaskentries = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500463
464 def runq_depends_names(self, ids):
465 import re
466 ret = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600467 for id in ids:
468 nam = os.path.basename(id)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500469 nam = re.sub("_[^,]*,", ",", nam)
470 ret.extend([nam])
471 return ret
472
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600473 def get_task_hash(self, tid):
474 return self.runtaskentries[tid].hash
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500475
Brad Bishop19323692019-04-05 15:28:33 -0400476 def get_task_unihash(self, tid):
477 return self.runtaskentries[tid].unihash
478
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600479 def get_user_idstring(self, tid, task_name_suffix = ""):
480 return tid + task_name_suffix
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500481
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500482 def get_short_user_idstring(self, task, task_name_suffix = ""):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500483 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
484 pn = self.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600485 taskname = taskname_from_tid(task) + task_name_suffix
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500486 return "%s:%s" % (pn, taskname)
487
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488 def circular_depchains_handler(self, tasks):
489 """
490 Some tasks aren't buildable, likely due to circular dependency issues.
491 Identify the circular dependencies and print them in a user readable format.
492 """
493 from copy import deepcopy
494
495 valid_chains = []
496 explored_deps = {}
497 msgs = []
498
Andrew Geissler99467da2019-02-25 18:54:23 -0600499 class TooManyLoops(Exception):
500 pass
501
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500502 def chain_reorder(chain):
503 """
504 Reorder a dependency chain so the lowest task id is first
505 """
506 lowest = 0
507 new_chain = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600508 for entry in range(len(chain)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500509 if chain[entry] < chain[lowest]:
510 lowest = entry
511 new_chain.extend(chain[lowest:])
512 new_chain.extend(chain[:lowest])
513 return new_chain
514
515 def chain_compare_equal(chain1, chain2):
516 """
517 Compare two dependency chains and see if they're the same
518 """
519 if len(chain1) != len(chain2):
520 return False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600521 for index in range(len(chain1)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500522 if chain1[index] != chain2[index]:
523 return False
524 return True
525
526 def chain_array_contains(chain, chain_array):
527 """
528 Return True if chain_array contains chain
529 """
530 for ch in chain_array:
531 if chain_compare_equal(ch, chain):
532 return True
533 return False
534
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600535 def find_chains(tid, prev_chain):
536 prev_chain.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500537 total_deps = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600538 total_deps.extend(self.runtaskentries[tid].revdeps)
539 for revdep in self.runtaskentries[tid].revdeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500540 if revdep in prev_chain:
541 idx = prev_chain.index(revdep)
542 # To prevent duplicates, reorder the chain to start with the lowest taskid
543 # and search through an array of those we've already printed
544 chain = prev_chain[idx:]
545 new_chain = chain_reorder(chain)
546 if not chain_array_contains(new_chain, valid_chains):
547 valid_chains.append(new_chain)
548 msgs.append("Dependency loop #%d found:\n" % len(valid_chains))
549 for dep in new_chain:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600550 msgs.append(" Task %s (dependent Tasks %s)\n" % (dep, self.runq_depends_names(self.runtaskentries[dep].depends)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500551 msgs.append("\n")
552 if len(valid_chains) > 10:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000553 msgs.append("Halted dependency loops search after 10 matches.\n")
Andrew Geissler99467da2019-02-25 18:54:23 -0600554 raise TooManyLoops
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500555 continue
556 scan = False
557 if revdep not in explored_deps:
558 scan = True
559 elif revdep in explored_deps[revdep]:
560 scan = True
561 else:
562 for dep in prev_chain:
563 if dep in explored_deps[revdep]:
564 scan = True
565 if scan:
566 find_chains(revdep, copy.deepcopy(prev_chain))
567 for dep in explored_deps[revdep]:
568 if dep not in total_deps:
569 total_deps.append(dep)
570
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600571 explored_deps[tid] = total_deps
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500572
Andrew Geissler99467da2019-02-25 18:54:23 -0600573 try:
574 for task in tasks:
575 find_chains(task, [])
576 except TooManyLoops:
577 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500578
579 return msgs
580
581 def calculate_task_weights(self, endpoints):
582 """
583 Calculate a number representing the "weight" of each task. Heavier weighted tasks
584 have more dependencies and hence should be executed sooner for maximum speed.
585
586 This function also sanity checks the task list finding tasks that are not
587 possible to execute due to circular dependencies.
588 """
589
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600590 numTasks = len(self.runtaskentries)
591 weight = {}
592 deps_left = {}
593 task_done = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500594
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600595 for tid in self.runtaskentries:
596 task_done[tid] = False
597 weight[tid] = 1
598 deps_left[tid] = len(self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500599
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600600 for tid in endpoints:
601 weight[tid] = 10
602 task_done[tid] = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500603
604 while True:
605 next_points = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600606 for tid in endpoints:
607 for revdep in self.runtaskentries[tid].depends:
608 weight[revdep] = weight[revdep] + weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500609 deps_left[revdep] = deps_left[revdep] - 1
610 if deps_left[revdep] == 0:
611 next_points.append(revdep)
612 task_done[revdep] = True
613 endpoints = next_points
Andrew Geissler595f6302022-01-24 19:11:47 +0000614 if not next_points:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500615 break
616
617 # Circular dependency sanity check
618 problem_tasks = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600619 for tid in self.runtaskentries:
620 if task_done[tid] is False or deps_left[tid] != 0:
621 problem_tasks.append(tid)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600622 logger.debug2("Task %s is not buildable", tid)
623 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 -0600624 self.runtaskentries[tid].weight = weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500625
626 if problem_tasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600627 message = "%s unbuildable tasks were found.\n" % len(problem_tasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500628 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"
629 message = message + "Identifying dependency loops (this may take a short while)...\n"
630 logger.error(message)
631
632 msgs = self.circular_depchains_handler(problem_tasks)
633
634 message = "\n"
635 for msg in msgs:
636 message = message + msg
637 bb.msg.fatal("RunQueue", message)
638
639 return weight
640
641 def prepare(self):
642 """
643 Turn a set of taskData into a RunQueue and compute data needed
644 to optimise the execution order.
645 """
646
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600647 runq_build = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500648 recursivetasks = {}
649 recursiveitasks = {}
650 recursivetasksselfref = set()
651
652 taskData = self.taskData
653
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600654 found = False
655 for mc in self.taskData:
Andrew Geissler595f6302022-01-24 19:11:47 +0000656 if taskData[mc].taskentries:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600657 found = True
658 break
659 if not found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500660 # Nothing to do
661 return 0
662
Andrew Geissler517393d2023-01-13 08:55:19 -0600663 bb.parse.siggen.setup_datacache(self.dataCaches)
664
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600665 self.init_progress_reporter.start()
666 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600667 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500668
669 # Step A - Work out a list of tasks to run
670 #
671 # Taskdata gives us a list of possible providers for every build and run
672 # target ordered by priority. It also gives information on each of those
673 # providers.
674 #
675 # To create the actual list of tasks to execute we fix the list of
676 # providers and then resolve the dependencies into task IDs. This
677 # process is repeated for each type of dependency (tdepends, deptask,
678 # rdeptast, recrdeptask, idepends).
679
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600680 def add_build_dependencies(depids, tasknames, depends, mc):
681 for depname in depids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500682 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600683 if depname not in taskData[mc].build_targets or not taskData[mc].build_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500684 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600685 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500686 if depdata is None:
687 continue
688 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600689 t = depdata + ":" + taskname
690 if t in taskData[mc].taskentries:
691 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500692
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600693 def add_runtime_dependencies(depids, tasknames, depends, mc):
694 for depname in depids:
695 if depname not in taskData[mc].run_targets or not taskData[mc].run_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500696 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600697 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500698 if depdata is None:
699 continue
700 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600701 t = depdata + ":" + taskname
702 if t in taskData[mc].taskentries:
703 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500704
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800705 def add_mc_dependencies(mc, tid):
706 mcdeps = taskData[mc].get_mcdepends()
707 for dep in mcdeps:
708 mcdependency = dep.split(':')
709 pn = mcdependency[3]
710 frommc = mcdependency[1]
711 mcdep = mcdependency[2]
712 deptask = mcdependency[4]
Andrew Geissler517393d2023-01-13 08:55:19 -0600713 if mcdep not in taskData:
714 bb.fatal("Multiconfig '%s' is referenced in multiconfig dependency '%s' but not enabled in BBMULTICONFIG?" % (mcdep, dep))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800715 if mc == frommc:
716 fn = taskData[mcdep].build_targets[pn][0]
717 newdep = '%s:%s' % (fn,deptask)
718 taskData[mc].taskentries[tid].tdepends.append(newdep)
719
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600720 for mc in taskData:
721 for tid in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500722
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
724 #runtid = build_tid(mc, fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600726 #logger.debug2("Processing %s,%s:%s", mc, fn, taskname)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600727
728 depends = set()
729 task_deps = self.dataCaches[mc].task_deps[taskfn]
730
731 self.runtaskentries[tid] = RunTaskEntry()
732
733 if fn in taskData[mc].failed_fns:
734 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500735
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800736 # We add multiconfig dependencies before processing internal task deps (tdepends)
737 if 'mcdepends' in task_deps and taskname in task_deps['mcdepends']:
738 add_mc_dependencies(mc, tid)
739
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500740 # Resolve task internal dependencies
741 #
742 # e.g. addtask before X after Y
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600743 for t in taskData[mc].taskentries[tid].tdepends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800744 (depmc, depfn, deptaskname, _) = split_tid_mcfn(t)
745 depends.add(build_tid(depmc, depfn, deptaskname))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746
747 # Resolve 'deptask' dependencies
748 #
749 # e.g. do_sometask[deptask] = "do_someothertask"
750 # (makes sure sometask runs after someothertask of all DEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600751 if 'deptask' in task_deps and taskname in task_deps['deptask']:
752 tasknames = task_deps['deptask'][taskname].split()
753 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500754
755 # Resolve 'rdeptask' dependencies
756 #
757 # e.g. do_sometask[rdeptask] = "do_someothertask"
758 # (makes sure sometask runs after someothertask of all RDEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600759 if 'rdeptask' in task_deps and taskname in task_deps['rdeptask']:
760 tasknames = task_deps['rdeptask'][taskname].split()
761 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500762
763 # Resolve inter-task dependencies
764 #
765 # e.g. do_sometask[depends] = "targetname:do_someothertask"
766 # (makes sure sometask runs after targetname's someothertask)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600767 idepends = taskData[mc].taskentries[tid].idepends
768 for (depname, idependtask) in idepends:
769 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 -0500770 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600771 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500772 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600773 t = depdata + ":" + idependtask
774 depends.add(t)
775 if t not in taskData[mc].taskentries:
776 bb.msg.fatal("RunQueue", "Task %s in %s depends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
777 irdepends = taskData[mc].taskentries[tid].irdepends
778 for (depname, idependtask) in irdepends:
779 if depname in taskData[mc].run_targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500780 # Won't be in run_targets if ASSUME_PROVIDED
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500781 if not taskData[mc].run_targets[depname]:
782 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600783 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500784 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600785 t = depdata + ":" + idependtask
786 depends.add(t)
787 if t not in taskData[mc].taskentries:
788 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 -0500789
790 # Resolve recursive 'recrdeptask' dependencies (Part A)
791 #
792 # e.g. do_sometask[recrdeptask] = "do_someothertask"
793 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
794 # We cover the recursive part of the dependencies below
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600795 if 'recrdeptask' in task_deps and taskname in task_deps['recrdeptask']:
796 tasknames = task_deps['recrdeptask'][taskname].split()
797 recursivetasks[tid] = tasknames
798 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
799 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
800 if taskname in tasknames:
801 recursivetasksselfref.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500802
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600803 if 'recideptask' in task_deps and taskname in task_deps['recideptask']:
804 recursiveitasks[tid] = []
805 for t in task_deps['recideptask'][taskname].split():
806 newdep = build_tid(mc, fn, t)
807 recursiveitasks[tid].append(newdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500808
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600809 self.runtaskentries[tid].depends = depends
Brad Bishop316dfdd2018-06-25 12:45:53 -0400810 # Remove all self references
811 self.runtaskentries[tid].depends.discard(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500812
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600813 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500814
Brad Bishop316dfdd2018-06-25 12:45:53 -0400815 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600816 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400817
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500818 # Resolve recursive 'recrdeptask' dependencies (Part B)
819 #
820 # e.g. do_sometask[recrdeptask] = "do_someothertask"
821 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600822 # 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 -0600823
Brad Bishop316dfdd2018-06-25 12:45:53 -0400824 # Generating/interating recursive lists of dependencies is painful and potentially slow
825 # Precompute recursive task dependencies here by:
826 # a) create a temp list of reverse dependencies (revdeps)
827 # b) walk up the ends of the chains (when a given task no longer has dependencies i.e. len(deps) == 0)
828 # c) combine the total list of dependencies in cumulativedeps
829 # d) optimise by pre-truncating 'task' off the items in cumulativedeps (keeps items in sets lower)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500830
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500831
Brad Bishop316dfdd2018-06-25 12:45:53 -0400832 revdeps = {}
833 deps = {}
834 cumulativedeps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600835 for tid in self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400836 deps[tid] = set(self.runtaskentries[tid].depends)
837 revdeps[tid] = set()
838 cumulativedeps[tid] = set()
839 # Generate a temp list of reverse dependencies
840 for tid in self.runtaskentries:
841 for dep in self.runtaskentries[tid].depends:
842 revdeps[dep].add(tid)
843 # Find the dependency chain endpoints
844 endpoints = set()
845 for tid in self.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +0000846 if not deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400847 endpoints.add(tid)
848 # Iterate the chains collating dependencies
849 while endpoints:
850 next = set()
851 for tid in endpoints:
852 for dep in revdeps[tid]:
853 cumulativedeps[dep].add(fn_from_tid(tid))
854 cumulativedeps[dep].update(cumulativedeps[tid])
855 if tid in deps[dep]:
856 deps[dep].remove(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +0000857 if not deps[dep]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400858 next.add(dep)
859 endpoints = next
860 #for tid in deps:
Andrew Geissler595f6302022-01-24 19:11:47 +0000861 # if deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400862 # bb.warn("Sanity test failure, dependencies left for %s (%s)" % (tid, deps[tid]))
863
864 # Loop here since recrdeptasks can depend upon other recrdeptasks and we have to
865 # resolve these recursively until we aren't adding any further extra dependencies
866 extradeps = True
867 while extradeps:
868 extradeps = 0
869 for tid in recursivetasks:
870 tasknames = recursivetasks[tid]
871
872 totaldeps = set(self.runtaskentries[tid].depends)
873 if tid in recursiveitasks:
874 totaldeps.update(recursiveitasks[tid])
875 for dep in recursiveitasks[tid]:
876 if dep not in self.runtaskentries:
877 continue
878 totaldeps.update(self.runtaskentries[dep].depends)
879
880 deps = set()
881 for dep in totaldeps:
882 if dep in cumulativedeps:
883 deps.update(cumulativedeps[dep])
884
885 for t in deps:
886 for taskname in tasknames:
887 newtid = t + ":" + taskname
888 if newtid == tid:
889 continue
890 if newtid in self.runtaskentries and newtid not in self.runtaskentries[tid].depends:
891 extradeps += 1
892 self.runtaskentries[tid].depends.add(newtid)
893
894 # Handle recursive tasks which depend upon other recursive tasks
895 deps = set()
896 for dep in self.runtaskentries[tid].depends.intersection(recursivetasks):
897 deps.update(self.runtaskentries[dep].depends.difference(self.runtaskentries[tid].depends))
898 for newtid in deps:
899 for taskname in tasknames:
900 if not newtid.endswith(":" + taskname):
901 continue
902 if newtid in self.runtaskentries:
903 extradeps += 1
904 self.runtaskentries[tid].depends.add(newtid)
905
906 bb.debug(1, "Added %s recursive dependencies in this loop" % extradeps)
907
908 # Remove recrdeptask circular references so that do_a[recrdeptask] = "do_a do_b" can work
909 for tid in recursivetasksselfref:
910 self.runtaskentries[tid].depends.difference_update(recursivetasksselfref)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600911
912 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600913 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600914
915 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500916
917 # Step B - Mark all active tasks
918 #
919 # Start with the tasks we were asked to run and mark all dependencies
920 # as active too. If the task is to be 'forced', clear its stamp. Once
921 # all active tasks are marked, prune the ones we don't need.
922
923 logger.verbose("Marking Active Tasks")
924
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600925 def mark_active(tid, depth):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500926 """
927 Mark an item as active along with its depends
928 (calls itself recursively)
929 """
930
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600931 if tid in runq_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500932 return
933
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600934 runq_build[tid] = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500935
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600936 depends = self.runtaskentries[tid].depends
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500937 for depend in depends:
938 mark_active(depend, depth+1)
939
Brad Bishop79641f22019-09-10 07:20:22 -0400940 def invalidate_task(tid, error_nostamp):
941 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
942 taskdep = self.dataCaches[mc].task_deps[taskfn]
943 if fn + ":" + taskname not in taskData[mc].taskentries:
944 logger.warning("Task %s does not exist, invalidating this task will have no effect" % taskname)
945 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
946 if error_nostamp:
947 bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
948 else:
949 bb.debug(1, "Task %s is marked nostamp, cannot invalidate this task" % taskname)
950 else:
951 logger.verbose("Invalidate task %s, %s", taskname, fn)
Andrew Geissler517393d2023-01-13 08:55:19 -0600952 bb.parse.siggen.invalidate_task(taskname, taskfn)
Brad Bishop79641f22019-09-10 07:20:22 -0400953
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600954 self.target_tids = []
955 for (mc, target, task, fn) in self.targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500956
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600957 if target not in taskData[mc].build_targets or not taskData[mc].build_targets[target]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500958 continue
959
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600960 if target in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500961 continue
962
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500963 parents = False
964 if task.endswith('-'):
965 parents = True
966 task = task[:-1]
967
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600968 if fn in taskData[mc].failed_fns:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500969 continue
970
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600971 # fn already has mc prefix
972 tid = fn + ":" + task
973 self.target_tids.append(tid)
974 if tid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500975 import difflib
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600976 tasks = []
977 for x in taskData[mc].taskentries:
978 if x.startswith(fn + ":"):
979 tasks.append(taskname_from_tid(x))
980 close_matches = difflib.get_close_matches(task, tasks, cutoff=0.7)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500981 if close_matches:
982 extra = ". Close matches:\n %s" % "\n ".join(close_matches)
983 else:
984 extra = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600985 bb.msg.fatal("RunQueue", "Task %s does not exist for target %s (%s)%s" % (task, target, tid, extra))
986
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500987 # For tasks called "XXXX-", ony run their dependencies
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500988 if parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600989 for i in self.runtaskentries[tid].depends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500990 mark_active(i, 1)
991 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600992 mark_active(tid, 1)
993
994 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600995 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500996
997 # Step C - Prune all inactive tasks
998 #
999 # Once all active tasks are marked, prune the ones we don't need.
1000
Brad Bishop316dfdd2018-06-25 12:45:53 -04001001 # Handle --runall
1002 if self.cooker.configuration.runall:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001003 # re-run the mark_active and then drop unused tasks from new list
Andrew Geissler595f6302022-01-24 19:11:47 +00001004 reduced_tasklist = set(self.runtaskentries.keys())
1005 for tid in list(self.runtaskentries.keys()):
1006 if tid not in runq_build:
1007 reduced_tasklist.remove(tid)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001008 runq_build = {}
Brad Bishop316dfdd2018-06-25 12:45:53 -04001009
1010 for task in self.cooker.configuration.runall:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001011 if not task.startswith("do_"):
1012 task = "do_{0}".format(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001013 runall_tids = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00001014 for tid in reduced_tasklist:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001015 wanttid = "{0}:{1}".format(fn_from_tid(tid), task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001016 if wanttid in self.runtaskentries:
1017 runall_tids.add(wanttid)
1018
1019 for tid in list(runall_tids):
Andrew Geissler595f6302022-01-24 19:11:47 +00001020 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001021 if self.cooker.configuration.force:
1022 invalidate_task(tid, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001023
Andrew Geissler595f6302022-01-24 19:11:47 +00001024 delcount = set()
1025 for tid in list(self.runtaskentries.keys()):
1026 if tid not in runq_build:
1027 delcount.add(tid)
1028 del self.runtaskentries[tid]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001029
Andrew Geissler595f6302022-01-24 19:11:47 +00001030 if self.cooker.configuration.runall:
1031 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001032 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)))
1033
1034 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001035 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001036
1037 # Handle runonly
1038 if self.cooker.configuration.runonly:
1039 # re-run the mark_active and then drop unused tasks from new list
1040 runq_build = {}
1041
1042 for task in self.cooker.configuration.runonly:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001043 if not task.startswith("do_"):
1044 task = "do_{0}".format(task)
Andrew Geissler595f6302022-01-24 19:11:47 +00001045 runonly_tids = [k for k in self.runtaskentries.keys() if taskname_from_tid(k) == task]
Brad Bishop316dfdd2018-06-25 12:45:53 -04001046
Andrew Geissler595f6302022-01-24 19:11:47 +00001047 for tid in runonly_tids:
1048 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001049 if self.cooker.configuration.force:
1050 invalidate_task(tid, False)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001051
1052 for tid in list(self.runtaskentries.keys()):
1053 if tid not in runq_build:
Andrew Geissler595f6302022-01-24 19:11:47 +00001054 delcount.add(tid)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001055 del self.runtaskentries[tid]
1056
Andrew Geissler595f6302022-01-24 19:11:47 +00001057 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001058 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 -05001059
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001060 #
1061 # Step D - Sanity checks and computation
1062 #
1063
1064 # Check to make sure we still have tasks to run
Andrew Geissler595f6302022-01-24 19:11:47 +00001065 if not self.runtaskentries:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001066 if not taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001067 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.")
1068 else:
1069 bb.msg.fatal("RunQueue", "No active tasks and not in --continue mode?! Please report this bug.")
1070
Brad Bishop316dfdd2018-06-25 12:45:53 -04001071 logger.verbose("Pruned %s inactive tasks, %s left", len(delcount), len(self.runtaskentries))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001072
1073 logger.verbose("Assign Weightings")
1074
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001075 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001076 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001077
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001078 # Generate a list of reverse dependencies to ease future calculations
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001079 for tid in self.runtaskentries:
1080 for dep in self.runtaskentries[tid].depends:
1081 self.runtaskentries[dep].revdeps.add(tid)
1082
1083 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001084 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001085
1086 # Identify tasks at the end of dependency chains
1087 # Error on circular dependency loops (length two)
1088 endpoints = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001089 for tid in self.runtaskentries:
1090 revdeps = self.runtaskentries[tid].revdeps
Andrew Geissler595f6302022-01-24 19:11:47 +00001091 if not revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001092 endpoints.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001093 for dep in revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001094 if dep in self.runtaskentries[tid].depends:
1095 bb.msg.fatal("RunQueue", "Task %s has circular dependency on %s" % (tid, dep))
1096
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001097
1098 logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
1099
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001100 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001101 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001102
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001103 # Calculate task weights
1104 # Check of higher length circular dependencies
1105 self.runq_weight = self.calculate_task_weights(endpoints)
1106
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001107 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001108 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001109
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001110 # Sanity Check - Check for multiple tasks building the same provider
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001111 for mc in self.dataCaches:
1112 prov_list = {}
1113 seen_fn = []
1114 for tid in self.runtaskentries:
1115 (tidmc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1116 if taskfn in seen_fn:
1117 continue
1118 if mc != tidmc:
1119 continue
1120 seen_fn.append(taskfn)
1121 for prov in self.dataCaches[mc].fn_provides[taskfn]:
1122 if prov not in prov_list:
1123 prov_list[prov] = [taskfn]
1124 elif taskfn not in prov_list[prov]:
1125 prov_list[prov].append(taskfn)
1126 for prov in prov_list:
1127 if len(prov_list[prov]) < 2:
1128 continue
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001129 if prov in self.multi_provider_allowed:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001130 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001131 seen_pn = []
1132 # If two versions of the same PN are being built its fatal, we don't support it.
1133 for fn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001134 pn = self.dataCaches[mc].pkg_fn[fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001135 if pn not in seen_pn:
1136 seen_pn.append(pn)
1137 else:
1138 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 +00001139 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 -05001140 #
1141 # Construct a list of things which uniquely depend on each provider
1142 # since this may help the user figure out which dependency is triggering this warning
1143 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001144 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 -05001145 deplist = {}
1146 commondeps = None
1147 for provfn in prov_list[prov]:
1148 deps = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001149 for tid in self.runtaskentries:
1150 fn = fn_from_tid(tid)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001151 if fn != provfn:
1152 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001153 for dep in self.runtaskentries[tid].revdeps:
1154 fn = fn_from_tid(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001155 if fn == provfn:
1156 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001157 deps.add(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001158 if not commondeps:
1159 commondeps = set(deps)
1160 else:
1161 commondeps &= deps
1162 deplist[provfn] = deps
1163 for provfn in deplist:
Andrew Geissler595f6302022-01-24 19:11:47 +00001164 msgs.append("\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001165 #
1166 # Construct a list of provides and runtime providers for each recipe
1167 # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC)
1168 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001169 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 -05001170 provide_results = {}
1171 rprovide_results = {}
1172 commonprovs = None
1173 commonrprovs = None
1174 for provfn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001175 provides = set(self.dataCaches[mc].fn_provides[provfn])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001176 rprovides = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001177 for rprovide in self.dataCaches[mc].rproviders:
1178 if provfn in self.dataCaches[mc].rproviders[rprovide]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001179 rprovides.add(rprovide)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001180 for package in self.dataCaches[mc].packages:
1181 if provfn in self.dataCaches[mc].packages[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001182 rprovides.add(package)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001183 for package in self.dataCaches[mc].packages_dynamic:
1184 if provfn in self.dataCaches[mc].packages_dynamic[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001185 rprovides.add(package)
1186 if not commonprovs:
1187 commonprovs = set(provides)
1188 else:
1189 commonprovs &= provides
1190 provide_results[provfn] = provides
1191 if not commonrprovs:
1192 commonrprovs = set(rprovides)
1193 else:
1194 commonrprovs &= rprovides
1195 rprovide_results[provfn] = rprovides
Andrew Geissler595f6302022-01-24 19:11:47 +00001196 #msgs.append("\nCommon provides:\n %s" % ("\n ".join(commonprovs)))
1197 #msgs.append("\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001198 for provfn in prov_list[prov]:
Andrew Geissler595f6302022-01-24 19:11:47 +00001199 msgs.append("\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs)))
1200 msgs.append("\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001201
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001202 if self.warn_multi_bb:
Andrew Geissler595f6302022-01-24 19:11:47 +00001203 logger.verbnote("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001204 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00001205 logger.error("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001206
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001207 self.init_progress_reporter.next_stage()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001208 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001209 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001210
1211 # Iterate over the task list looking for tasks with a 'setscene' function
Andrew Geissler82c905d2020-04-13 13:39:40 -05001212 self.runq_setscene_tids = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001213 if not self.cooker.configuration.nosetscene:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001214 for tid in self.runtaskentries:
1215 (mc, fn, taskname, _) = split_tid_mcfn(tid)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001216 setscenetid = tid + "_setscene"
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001217 if setscenetid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001218 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -05001219 self.runq_setscene_tids.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001220
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001221 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001222 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001223
1224 # Invalidate task if force mode active
1225 if self.cooker.configuration.force:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001226 for tid in self.target_tids:
1227 invalidate_task(tid, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001228
1229 # Invalidate task if invalidate mode active
1230 if self.cooker.configuration.invalidate_stamp:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001231 for tid in self.target_tids:
1232 fn = fn_from_tid(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001233 for st in self.cooker.configuration.invalidate_stamp.split(','):
1234 if not st.startswith("do_"):
1235 st = "do_%s" % st
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001236 invalidate_task(fn + ":" + st, True)
1237
1238 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001239 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001240
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001241 # Create and print to the logs a virtual/xxxx -> PN (fn) table
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001242 for mc in taskData:
1243 virtmap = taskData[mc].get_providermap(prefix="virtual/")
1244 virtpnmap = {}
1245 for v in virtmap:
1246 virtpnmap[v] = self.dataCaches[mc].pkg_fn[virtmap[v]]
1247 bb.debug(2, "%s resolved to: %s (%s)" % (v, virtpnmap[v], virtmap[v]))
1248 if hasattr(bb.parse.siggen, "tasks_resolved"):
1249 bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
1250
1251 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001252 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001253
Brad Bishop00e122a2019-10-05 11:10:57 -04001254 bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
1255
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001256 # Iterate over the task list and call into the siggen code
1257 dealtwith = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001258 todeal = set(self.runtaskentries)
Andrew Geissler595f6302022-01-24 19:11:47 +00001259 while todeal:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001260 for tid in todeal.copy():
Andrew Geissler595f6302022-01-24 19:11:47 +00001261 if not (self.runtaskentries[tid].depends - dealtwith):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001262 dealtwith.add(tid)
1263 todeal.remove(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001264 self.prepare_task_hash(tid)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001265 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001266
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001267 bb.parse.siggen.writeout_file_checksum_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001268
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001269 #self.dump_data()
1270 return len(self.runtaskentries)
1271
Brad Bishop19323692019-04-05 15:28:33 -04001272 def prepare_task_hash(self, tid):
Andrew Geissler517393d2023-01-13 08:55:19 -06001273 bb.parse.siggen.prep_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
1274 self.runtaskentries[tid].hash = bb.parse.siggen.get_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
Brad Bishop08902b02019-08-20 09:16:51 -04001275 self.runtaskentries[tid].unihash = bb.parse.siggen.get_unihash(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001276
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001277 def dump_data(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001278 """
1279 Dump some debug information on the internal data structures
1280 """
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001281 logger.debug3("run_tasks:")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001282 for tid in self.runtaskentries:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001283 logger.debug3(" %s: %s Deps %s RevDeps %s", tid,
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001284 self.runtaskentries[tid].weight,
1285 self.runtaskentries[tid].depends,
1286 self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001287
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001288class RunQueueWorker():
1289 def __init__(self, process, pipe):
1290 self.process = process
1291 self.pipe = pipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001292
1293class RunQueue:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001294 def __init__(self, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001295
1296 self.cooker = cooker
1297 self.cfgData = cfgData
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001298 self.rqdata = RunQueueData(self, cooker, cfgData, dataCaches, taskData, targets)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001299
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001300 self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION") or None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001301 self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID") or None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001302
1303 self.state = runQueuePrepare
1304
1305 # For disk space monitor
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001306 # Invoked at regular time intervals via the bitbake heartbeat event
1307 # while the build is running. We generate a unique name for the handler
1308 # here, just in case that there ever is more than one RunQueue instance,
Brad Bishop96ff1982019-08-19 13:50:42 -04001309 # start the handler when reaching runQueueSceneInit, and stop it when
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001310 # done with the build.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001311 self.dm = monitordisk.diskMonitor(cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001312 self.dm_event_handler_name = '_bb_diskmonitor_' + str(id(self))
1313 self.dm_event_handler_registered = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001314 self.rqexe = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001315 self.worker = {}
1316 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001317
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001318 def _start_worker(self, mc, fakeroot = False, rqexec = None):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001319 logger.debug("Starting bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001320 magic = "decafbad"
1321 if self.cooker.configuration.profile:
1322 magic = "decafbadbad"
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001323 fakerootlogs = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001324 if fakeroot:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001325 magic = magic + "beef"
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001326 mcdata = self.cooker.databuilder.mcdata[mc]
Brad Bishop19323692019-04-05 15:28:33 -04001327 fakerootcmd = shlex.split(mcdata.getVar("FAKEROOTCMD"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001328 fakerootenv = (mcdata.getVar("FAKEROOTBASEENV") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001329 env = os.environ.copy()
1330 for key, value in (var.split('=') for var in fakerootenv):
1331 env[key] = value
Brad Bishop19323692019-04-05 15:28:33 -04001332 worker = subprocess.Popen(fakerootcmd + ["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001333 fakerootlogs = self.rqdata.dataCaches[mc].fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334 else:
1335 worker = subprocess.Popen(["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
1336 bb.utils.nonblockingfd(worker.stdout)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001337 workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, self, rqexec, fakerootlogs=fakerootlogs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001338
1339 workerdata = {
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001340 "sigdata" : bb.parse.siggen.get_taskdata(),
Andrew Geissler82c905d2020-04-13 13:39:40 -05001341 "logdefaultlevel" : bb.msg.loggerDefaultLogLevel,
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001342 "build_verbose_shell" : self.cooker.configuration.build_verbose_shell,
1343 "build_verbose_stdout" : self.cooker.configuration.build_verbose_stdout,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001344 "logdefaultdomain" : bb.msg.loggerDefaultDomains,
1345 "prhost" : self.cooker.prhost,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001346 "buildname" : self.cfgData.getVar("BUILDNAME"),
1347 "date" : self.cfgData.getVar("DATE"),
1348 "time" : self.cfgData.getVar("TIME"),
Brad Bishopa34c0302019-09-23 22:34:48 -04001349 "hashservaddr" : self.cooker.hashservaddr,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001350 "umask" : self.cfgData.getVar("BB_DEFAULT_UMASK"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001351 }
1352
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001353 worker.stdin.write(b"<cookerconfig>" + pickle.dumps(self.cooker.configuration) + b"</cookerconfig>")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001354 worker.stdin.write(b"<extraconfigdata>" + pickle.dumps(self.cooker.extraconfigdata) + b"</extraconfigdata>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001355 worker.stdin.write(b"<workerdata>" + pickle.dumps(workerdata) + b"</workerdata>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001356 worker.stdin.flush()
1357
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001358 return RunQueueWorker(worker, workerpipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001359
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001360 def _teardown_worker(self, worker):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001361 if not worker:
1362 return
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001363 logger.debug("Teardown for bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001364 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001365 worker.process.stdin.write(b"<quit></quit>")
1366 worker.process.stdin.flush()
1367 worker.process.stdin.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001368 except IOError:
1369 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001370 while worker.process.returncode is None:
1371 worker.pipe.read()
1372 worker.process.poll()
1373 while worker.pipe.read():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001374 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001375 worker.pipe.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001376
1377 def start_worker(self):
1378 if self.worker:
1379 self.teardown_workers()
1380 self.teardown = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001381 for mc in self.rqdata.dataCaches:
1382 self.worker[mc] = self._start_worker(mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001383
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001384 def start_fakeworker(self, rqexec, mc):
1385 if not mc in self.fakeworker:
1386 self.fakeworker[mc] = self._start_worker(mc, True, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001387
1388 def teardown_workers(self):
1389 self.teardown = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001390 for mc in self.worker:
1391 self._teardown_worker(self.worker[mc])
1392 self.worker = {}
1393 for mc in self.fakeworker:
1394 self._teardown_worker(self.fakeworker[mc])
1395 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001396
1397 def read_workers(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001398 for mc in self.worker:
1399 self.worker[mc].pipe.read()
1400 for mc in self.fakeworker:
1401 self.fakeworker[mc].pipe.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001402
1403 def active_fds(self):
1404 fds = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001405 for mc in self.worker:
1406 fds.append(self.worker[mc].pipe.input)
1407 for mc in self.fakeworker:
1408 fds.append(self.fakeworker[mc].pipe.input)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001409 return fds
1410
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001411 def check_stamp_task(self, tid, taskname = None, recurse = False, cache = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001412 def get_timestamp(f):
1413 try:
1414 if not os.access(f, os.F_OK):
1415 return None
1416 return os.stat(f)[stat.ST_MTIME]
1417 except:
1418 return None
1419
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001420 (mc, fn, tn, taskfn) = split_tid_mcfn(tid)
1421 if taskname is None:
1422 taskname = tn
1423
Andrew Geissler517393d2023-01-13 08:55:19 -06001424 stampfile = bb.parse.siggen.stampfile_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001425
1426 # If the stamp is missing, it's not current
1427 if not os.access(stampfile, os.F_OK):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001428 logger.debug2("Stampfile %s not available", stampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001429 return False
1430 # If it's a 'nostamp' task, it's not current
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001431 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001432 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001433 logger.debug2("%s.%s is nostamp\n", fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001434 return False
1435
Andrew Geissler517393d2023-01-13 08:55:19 -06001436 if taskname.endswith("_setscene"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001437 return True
1438
1439 if cache is None:
1440 cache = {}
1441
1442 iscurrent = True
1443 t1 = get_timestamp(stampfile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001444 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001445 if iscurrent:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001446 (mc2, fn2, taskname2, taskfn2) = split_tid_mcfn(dep)
Andrew Geissler517393d2023-01-13 08:55:19 -06001447 stampfile2 = bb.parse.siggen.stampfile_mcfn(taskname2, taskfn2)
1448 stampfile3 = bb.parse.siggen.stampfile_mcfn(taskname2 + "_setscene", taskfn2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001449 t2 = get_timestamp(stampfile2)
1450 t3 = get_timestamp(stampfile3)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001451 if t3 and not t2:
1452 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001453 if t3 and t3 > t2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001454 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00001455 if fn == fn2:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001456 if not t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001457 logger.debug2('Stampfile %s does not exist', stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001458 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001459 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001460 if t1 < t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001461 logger.debug2('Stampfile %s < %s', stampfile, stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001462 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001463 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001464 if recurse and iscurrent:
1465 if dep in cache:
1466 iscurrent = cache[dep]
1467 if not iscurrent:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001468 logger.debug2('Stampfile for dependency %s:%s invalid (cached)' % (fn2, taskname2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001469 else:
1470 iscurrent = self.check_stamp_task(dep, recurse=True, cache=cache)
1471 cache[dep] = iscurrent
1472 if recurse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001473 cache[tid] = iscurrent
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001474 return iscurrent
1475
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001476 def validate_hashes(self, tocheck, data, currentcount=0, siginfo=False, summary=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04001477 valid = set()
1478 if self.hashvalidate:
Brad Bishop08902b02019-08-20 09:16:51 -04001479 sq_data = {}
1480 sq_data['hash'] = {}
1481 sq_data['hashfn'] = {}
1482 sq_data['unihash'] = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04001483 for tid in tocheck:
1484 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04001485 sq_data['hash'][tid] = self.rqdata.runtaskentries[tid].hash
1486 sq_data['hashfn'][tid] = self.rqdata.dataCaches[mc].hashfn[taskfn]
1487 sq_data['unihash'][tid] = self.rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -04001488
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001489 valid = self.validate_hash(sq_data, data, siginfo, currentcount, summary)
Brad Bishop96ff1982019-08-19 13:50:42 -04001490
1491 return valid
1492
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001493 def validate_hash(self, sq_data, d, siginfo, currentcount, summary):
1494 locs = {"sq_data" : sq_data, "d" : d, "siginfo" : siginfo, "currentcount" : currentcount, "summary" : summary}
Brad Bishop19323692019-04-05 15:28:33 -04001495
Brad Bishop08902b02019-08-20 09:16:51 -04001496 # Metadata has **kwargs so args can be added, sq_data can also gain new fields
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001497 call = self.hashvalidate + "(sq_data, d, siginfo=siginfo, currentcount=currentcount, summary=summary)"
Brad Bishop19323692019-04-05 15:28:33 -04001498
Brad Bishop19323692019-04-05 15:28:33 -04001499 return bb.utils.better_eval(call, locs)
1500
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001501 def _execute_runqueue(self):
1502 """
1503 Run the tasks in a queue prepared by rqdata.prepare()
1504 Upon failure, optionally try to recover the build using any alternate providers
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001505 (if the halt on failure configuration option isn't set)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001506 """
1507
1508 retval = True
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001509 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001510
1511 if self.state is runQueuePrepare:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001512 # NOTE: if you add, remove or significantly refactor the stages of this
1513 # process then you should recalculate the weightings here. This is quite
1514 # easy to do - just change the next line temporarily to pass debug=True as
1515 # the last parameter and you'll get a printout of the weightings as well
1516 # as a map to the lines where next_stage() was called. Of course this isn't
1517 # critical, but it helps to keep the progress reporting accurate.
1518 self.rqdata.init_progress_reporter = bb.progress.MultiStageProcessProgressReporter(self.cooker.data,
1519 "Initialising tasks",
1520 [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 -05001521 if self.rqdata.prepare() == 0:
1522 self.state = runQueueComplete
1523 else:
1524 self.state = runQueueSceneInit
Brad Bishop00e122a2019-10-05 11:10:57 -04001525 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001526
1527 if self.state is runQueueSceneInit:
Brad Bishop96ff1982019-08-19 13:50:42 -04001528 self.rqdata.init_progress_reporter.next_stage()
1529
1530 # we are ready to run, emit dependency info to any UI or class which
1531 # needs it
1532 depgraph = self.cooker.buildDependTree(self, self.rqdata.taskData)
1533 self.rqdata.init_progress_reporter.next_stage()
1534 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
1535
Brad Bishope2d5b612018-11-23 10:55:50 +13001536 if not self.dm_event_handler_registered:
1537 res = bb.event.register(self.dm_event_handler_name,
Andrew Geissler517393d2023-01-13 08:55:19 -06001538 lambda x, y: self.dm.check(self) if self.state in [runQueueRunning, runQueueCleanUp] else False,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001539 ('bb.event.HeartbeatEvent',), data=self.cfgData)
Brad Bishope2d5b612018-11-23 10:55:50 +13001540 self.dm_event_handler_registered = True
1541
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001542 dump = self.cooker.configuration.dump_signatures
1543 if dump:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001544 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001545 if 'printdiff' in dump:
1546 invalidtasks = self.print_diffscenetasks()
1547 self.dump_signatures(dump)
1548 if 'printdiff' in dump:
1549 self.write_diffscenetasks(invalidtasks)
1550 self.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001551
Brad Bishop96ff1982019-08-19 13:50:42 -04001552 if self.state is runQueueSceneInit:
1553 self.rqdata.init_progress_reporter.next_stage()
1554 self.start_worker()
1555 self.rqdata.init_progress_reporter.next_stage()
1556 self.rqexe = RunQueueExecute(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001557
Brad Bishop96ff1982019-08-19 13:50:42 -04001558 # If we don't have any setscene functions, skip execution
Andrew Geissler595f6302022-01-24 19:11:47 +00001559 if not self.rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04001560 logger.info('No setscene tasks')
1561 for tid in self.rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00001562 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04001563 self.rqexe.setbuildable(tid)
1564 self.rqexe.tasks_notcovered.add(tid)
1565 self.rqexe.sqdone = True
1566 logger.info('Executing Tasks')
1567 self.state = runQueueRunning
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001568
1569 if self.state is runQueueRunning:
1570 retval = self.rqexe.execute()
1571
1572 if self.state is runQueueCleanUp:
1573 retval = self.rqexe.finish()
1574
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001575 build_done = self.state is runQueueComplete or self.state is runQueueFailed
1576
1577 if build_done and self.dm_event_handler_registered:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001578 bb.event.remove(self.dm_event_handler_name, None, data=self.cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001579 self.dm_event_handler_registered = False
1580
1581 if build_done and self.rqexe:
Brad Bishop08902b02019-08-20 09:16:51 -04001582 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001583 self.teardown_workers()
Brad Bishop96ff1982019-08-19 13:50:42 -04001584 if self.rqexe:
1585 if self.rqexe.stats.failed:
1586 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)
1587 else:
1588 # Let's avoid the word "failed" if nothing actually did
1589 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 -05001590
1591 if self.state is runQueueFailed:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001592 raise bb.runqueue.TaskFailure(self.rqexe.failed_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001593
1594 if self.state is runQueueComplete:
1595 # All done
1596 return False
1597
1598 # Loop
1599 return retval
1600
1601 def execute_runqueue(self):
1602 # Catch unexpected exceptions and ensure we exit when an error occurs, not loop.
1603 try:
1604 return self._execute_runqueue()
1605 except bb.runqueue.TaskFailure:
1606 raise
1607 except SystemExit:
1608 raise
1609 except bb.BBHandledException:
1610 try:
1611 self.teardown_workers()
1612 except:
1613 pass
1614 self.state = runQueueComplete
1615 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001616 except Exception as err:
1617 logger.exception("An uncaught exception occurred in runqueue")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001618 try:
1619 self.teardown_workers()
1620 except:
1621 pass
1622 self.state = runQueueComplete
1623 raise
1624
1625 def finish_runqueue(self, now = False):
1626 if not self.rqexe:
1627 self.state = runQueueComplete
1628 return
1629
1630 if now:
1631 self.rqexe.finish_now()
1632 else:
1633 self.rqexe.finish()
1634
Andrew Geissler517393d2023-01-13 08:55:19 -06001635 def _rq_dump_sigtid(self, tids):
1636 for tid in tids:
1637 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1638 dataCaches = self.rqdata.dataCaches
1639 bb.parse.siggen.dump_sigtask(taskfn, taskname, dataCaches[mc].stamp[taskfn], True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001640
1641 def dump_signatures(self, options):
Andrew Geissler517393d2023-01-13 08:55:19 -06001642 if bb.cooker.CookerFeatures.RECIPE_SIGGEN_INFO not in self.cooker.featureset:
1643 bb.fatal("The dump signatures functionality needs the RECIPE_SIGGEN_INFO feature enabled")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001644
Andrew Geissler517393d2023-01-13 08:55:19 -06001645 bb.note("Writing task signature files")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001646
1647 max_process = int(self.cfgData.getVar("BB_NUMBER_PARSE_THREADS") or os.cpu_count() or 1)
Andrew Geissler517393d2023-01-13 08:55:19 -06001648 def chunkify(l, n):
1649 return [l[i::n] for i in range(n)]
1650 tids = chunkify(list(self.rqdata.runtaskentries), max_process)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001651 # We cannot use the real multiprocessing.Pool easily due to some local data
1652 # that can't be pickled. This is a cheap multi-process solution.
1653 launched = []
Andrew Geissler517393d2023-01-13 08:55:19 -06001654 while tids:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001655 if len(launched) < max_process:
Andrew Geissler517393d2023-01-13 08:55:19 -06001656 p = Process(target=self._rq_dump_sigtid, args=(tids.pop(), ))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001657 p.start()
1658 launched.append(p)
1659 for q in launched:
1660 # The finished processes are joined when calling is_alive()
1661 if not q.is_alive():
1662 launched.remove(q)
1663 for p in launched:
1664 p.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001665
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001666 bb.parse.siggen.dump_sigs(self.rqdata.dataCaches, options)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001667
1668 return
1669
1670 def print_diffscenetasks(self):
1671
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001672 noexec = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001673 tocheck = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001674
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001675 for tid in self.rqdata.runtaskentries:
1676 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1677 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001678
1679 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001680 noexec.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001681 continue
1682
Brad Bishop96ff1982019-08-19 13:50:42 -04001683 tocheck.add(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001684
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001685 valid_new = self.validate_hashes(tocheck, self.cooker.data, 0, True, summary=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001686
1687 # Tasks which are both setscene and noexec never care about dependencies
1688 # We therefore find tasks which are setscene and noexec and mark their
1689 # unique dependencies as valid.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001690 for tid in noexec:
1691 if tid not in self.rqdata.runq_setscene_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001692 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001693 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001694 hasnoexecparents = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001695 for dep2 in self.rqdata.runtaskentries[dep].revdeps:
1696 if dep2 in self.rqdata.runq_setscene_tids and dep2 in noexec:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001697 continue
1698 hasnoexecparents = False
1699 break
1700 if hasnoexecparents:
1701 valid_new.add(dep)
1702
1703 invalidtasks = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001704 for tid in self.rqdata.runtaskentries:
1705 if tid not in valid_new and tid not in noexec:
1706 invalidtasks.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001707
1708 found = set()
1709 processed = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001710 for tid in invalidtasks:
1711 toprocess = set([tid])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001712 while toprocess:
1713 next = set()
1714 for t in toprocess:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001715 for dep in self.rqdata.runtaskentries[t].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001716 if dep in invalidtasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001717 found.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001718 if dep not in processed:
1719 processed.add(dep)
1720 next.add(dep)
1721 toprocess = next
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001722 if tid in found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001723 toprocess = set()
1724
1725 tasklist = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001726 for tid in invalidtasks.difference(found):
1727 tasklist.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001728
1729 if tasklist:
1730 bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
1731
1732 return invalidtasks.difference(found)
1733
1734 def write_diffscenetasks(self, invalidtasks):
1735
1736 # Define recursion callback
1737 def recursecb(key, hash1, hash2):
1738 hashes = [hash1, hash2]
1739 hashfiles = bb.siggen.find_siginfo(key, None, hashes, self.cfgData)
1740
1741 recout = []
1742 if len(hashfiles) == 2:
1743 out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
Brad Bishopc342db32019-05-15 21:57:59 -04001744 recout.extend(list(' ' + l for l in out2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001745 else:
1746 recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
1747
1748 return recout
1749
1750
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001751 for tid in invalidtasks:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001752 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1753 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001754 h = self.rqdata.runtaskentries[tid].hash
Patrick Williams03907ee2022-05-01 06:28:52 -05001755 matches = bb.siggen.find_siginfo(pn, taskname, [], self.cooker.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001756 match = None
1757 for m in matches:
1758 if h in m:
1759 match = m
1760 if match is None:
1761 bb.fatal("Can't find a task we're supposed to have written out? (hash: %s)?" % h)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001762 matches = {k : v for k, v in iter(matches.items()) if h not in k}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001763 if matches:
1764 latestmatch = sorted(matches.keys(), key=lambda f: matches[f])[-1]
Brad Bishop19323692019-04-05 15:28:33 -04001765 prevh = __find_sha256__.search(latestmatch).group(0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001766 output = bb.siggen.compare_sigfiles(latestmatch, match, recursecb)
1767 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))
1768
Brad Bishop96ff1982019-08-19 13:50:42 -04001769
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001770class RunQueueExecute:
1771
1772 def __init__(self, rq):
1773 self.rq = rq
1774 self.cooker = rq.cooker
1775 self.cfgData = rq.cfgData
1776 self.rqdata = rq.rqdata
1777
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001778 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
1779 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001780 self.max_cpu_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_CPU")
1781 self.max_io_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_IO")
Patrick Williams92b42cb2022-09-03 06:53:57 -05001782 self.max_memory_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_MEMORY")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001783
Brad Bishop96ff1982019-08-19 13:50:42 -04001784 self.sq_buildable = set()
1785 self.sq_running = set()
1786 self.sq_live = set()
1787
Brad Bishop08902b02019-08-20 09:16:51 -04001788 self.updated_taskhash_queue = []
1789 self.pending_migrations = set()
1790
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001791 self.runq_buildable = set()
1792 self.runq_running = set()
1793 self.runq_complete = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001794 self.runq_tasksrun = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001795
1796 self.build_stamps = {}
1797 self.build_stamps2 = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001798 self.failed_tids = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001799 self.sq_deferred = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001800
1801 self.stampcache = {}
1802
Brad Bishop08902b02019-08-20 09:16:51 -04001803 self.holdoff_tasks = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04001804 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04001805 self.sqdone = False
1806
Andrew Geissler5199d832021-09-24 16:47:35 -05001807 self.stats = RunQueueStats(len(self.rqdata.runtaskentries), len(self.rqdata.runq_setscene_tids))
Brad Bishop96ff1982019-08-19 13:50:42 -04001808
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001809 for mc in rq.worker:
1810 rq.worker[mc].pipe.setrunqueueexec(self)
1811 for mc in rq.fakeworker:
1812 rq.fakeworker[mc].pipe.setrunqueueexec(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001813
1814 if self.number_tasks <= 0:
1815 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks)
1816
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001817 lower_limit = 1.0
1818 upper_limit = 1000000.0
1819 if self.max_cpu_pressure:
1820 self.max_cpu_pressure = float(self.max_cpu_pressure)
1821 if self.max_cpu_pressure < lower_limit:
1822 bb.fatal("Invalid BB_PRESSURE_MAX_CPU %s, minimum value is %s." % (self.max_cpu_pressure, lower_limit))
1823 if self.max_cpu_pressure > upper_limit:
1824 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))
1825
1826 if self.max_io_pressure:
1827 self.max_io_pressure = float(self.max_io_pressure)
1828 if self.max_io_pressure < lower_limit:
1829 bb.fatal("Invalid BB_PRESSURE_MAX_IO %s, minimum value is %s." % (self.max_io_pressure, lower_limit))
1830 if self.max_io_pressure > upper_limit:
1831 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))
1832
Patrick Williams92b42cb2022-09-03 06:53:57 -05001833 if self.max_memory_pressure:
1834 self.max_memory_pressure = float(self.max_memory_pressure)
1835 if self.max_memory_pressure < lower_limit:
1836 bb.fatal("Invalid BB_PRESSURE_MAX_MEMORY %s, minimum value is %s." % (self.max_memory_pressure, lower_limit))
1837 if self.max_memory_pressure > upper_limit:
1838 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))
1839
Brad Bishop96ff1982019-08-19 13:50:42 -04001840 # List of setscene tasks which we've covered
1841 self.scenequeue_covered = set()
1842 # List of tasks which are covered (including setscene ones)
1843 self.tasks_covered = set()
1844 self.tasks_scenequeue_done = set()
1845 self.scenequeue_notcovered = set()
1846 self.tasks_notcovered = set()
1847 self.scenequeue_notneeded = set()
1848
Brad Bishop08902b02019-08-20 09:16:51 -04001849 # We can't skip specified target tasks which aren't setscene tasks
1850 self.cantskip = set(self.rqdata.target_tids)
1851 self.cantskip.difference_update(self.rqdata.runq_setscene_tids)
1852 self.cantskip.intersection_update(self.rqdata.runtaskentries)
Brad Bishop96ff1982019-08-19 13:50:42 -04001853
1854 schedulers = self.get_schedulers()
1855 for scheduler in schedulers:
1856 if self.scheduler == scheduler.name:
1857 self.sched = scheduler(self, self.rqdata)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001858 logger.debug("Using runqueue scheduler '%s'", scheduler.name)
Brad Bishop96ff1982019-08-19 13:50:42 -04001859 break
1860 else:
1861 bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
1862 (self.scheduler, ", ".join(obj.name for obj in schedulers)))
1863
Andrew Geissler595f6302022-01-24 19:11:47 +00001864 #if self.rqdata.runq_setscene_tids:
Brad Bishop08902b02019-08-20 09:16:51 -04001865 self.sqdata = SQData()
1866 build_scenequeue_data(self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self)
Brad Bishop96ff1982019-08-19 13:50:42 -04001867
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001868 def runqueue_process_waitpid(self, task, status, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001869
1870 # self.build_stamps[pid] may not exist when use shared work directory.
1871 if task in self.build_stamps:
1872 self.build_stamps2.remove(self.build_stamps[task])
1873 del self.build_stamps[task]
1874
Brad Bishop96ff1982019-08-19 13:50:42 -04001875 if task in self.sq_live:
1876 if status != 0:
1877 self.sq_task_fail(task, status)
1878 else:
1879 self.sq_task_complete(task)
1880 self.sq_live.remove(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05001881 self.stats.updateActiveSetscene(len(self.sq_live))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001882 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04001883 if status != 0:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001884 self.task_fail(task, status, fakerootlog=fakerootlog)
Brad Bishop96ff1982019-08-19 13:50:42 -04001885 else:
1886 self.task_complete(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001887 return True
1888
1889 def finish_now(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001890 for mc in self.rq.worker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001891 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001892 self.rq.worker[mc].process.stdin.write(b"<finishnow></finishnow>")
1893 self.rq.worker[mc].process.stdin.flush()
1894 except IOError:
1895 # worker must have died?
1896 pass
1897 for mc in self.rq.fakeworker:
1898 try:
1899 self.rq.fakeworker[mc].process.stdin.write(b"<finishnow></finishnow>")
1900 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001901 except IOError:
1902 # worker must have died?
1903 pass
1904
Andrew Geissler595f6302022-01-24 19:11:47 +00001905 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001906 self.rq.state = runQueueFailed
1907 return
1908
1909 self.rq.state = runQueueComplete
1910 return
1911
1912 def finish(self):
1913 self.rq.state = runQueueCleanUp
1914
Andrew Geissler5199d832021-09-24 16:47:35 -05001915 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001916 if active > 0:
1917 bb.event.fire(runQueueExitWait(active), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001918 self.rq.read_workers()
1919 return self.rq.active_fds()
1920
Andrew Geissler595f6302022-01-24 19:11:47 +00001921 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001922 self.rq.state = runQueueFailed
1923 return True
1924
1925 self.rq.state = runQueueComplete
1926 return True
1927
Brad Bishop96ff1982019-08-19 13:50:42 -04001928 # Used by setscene only
1929 def check_dependencies(self, task, taskdeps):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001930 if not self.rq.depvalidate:
1931 return False
1932
Brad Bishop08902b02019-08-20 09:16:51 -04001933 # Must not edit parent data
1934 taskdeps = set(taskdeps)
1935
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001936 taskdata = {}
1937 taskdeps.add(task)
1938 for dep in taskdeps:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001939 (mc, fn, taskname, taskfn) = split_tid_mcfn(dep)
1940 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001941 taskdata[dep] = [pn, taskname, fn]
1942 call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001943 locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001944 valid = bb.utils.better_eval(call, locs)
1945 return valid
1946
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001947 def can_start_task(self):
Andrew Geissler5199d832021-09-24 16:47:35 -05001948 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001949 can_start = active < self.number_tasks
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001950 return can_start
1951
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001952 def get_schedulers(self):
1953 schedulers = set(obj for obj in globals().values()
1954 if type(obj) is type and
1955 issubclass(obj, RunQueueScheduler))
1956
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001957 user_schedulers = self.cfgData.getVar("BB_SCHEDULERS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001958 if user_schedulers:
1959 for sched in user_schedulers.split():
1960 if not "." in sched:
1961 bb.note("Ignoring scheduler '%s' from BB_SCHEDULERS: not an import" % sched)
1962 continue
1963
1964 modname, name = sched.rsplit(".", 1)
1965 try:
1966 module = __import__(modname, fromlist=(name,))
1967 except ImportError as exc:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001968 bb.fatal("Unable to import scheduler '%s' from '%s': %s" % (name, modname, exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001969 else:
1970 schedulers.add(getattr(module, name))
1971 return schedulers
1972
1973 def setbuildable(self, task):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001974 self.runq_buildable.add(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001975 self.sched.newbuildable(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001976
1977 def task_completeoutright(self, task):
1978 """
1979 Mark a task as completed
1980 Look at the reverse dependencies and mark any task with
1981 completed dependencies as buildable
1982 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001983 self.runq_complete.add(task)
1984 for revdep in self.rqdata.runtaskentries[task].revdeps:
1985 if revdep in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001986 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001987 if revdep in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001988 continue
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001989 alldeps = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001990 for dep in self.rqdata.runtaskentries[revdep].depends:
1991 if dep not in self.runq_complete:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001992 alldeps = False
1993 break
1994 if alldeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001995 self.setbuildable(revdep)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001996 logger.debug("Marking task %s as buildable", revdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001997
Andrew Geissler8f840682023-07-21 09:09:43 -05001998 found = None
1999 for t in sorted(self.sq_deferred.copy()):
Andrew Geissler5199d832021-09-24 16:47:35 -05002000 if self.sq_deferred[t] == task:
Andrew Geissler8f840682023-07-21 09:09:43 -05002001 # Allow the next deferred task to run. Any other deferred tasks should be deferred after that task.
2002 # We shouldn't allow all to run at once as it is prone to races.
2003 if not found:
2004 bb.debug(1, "Deferred task %s now buildable" % t)
2005 del self.sq_deferred[t]
2006 update_scenequeue_data([t], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
2007 found = t
2008 else:
2009 bb.debug(1, "Deferring %s after %s" % (t, found))
2010 self.sq_deferred[t] = found
Andrew Geissler5199d832021-09-24 16:47:35 -05002011
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002012 def task_complete(self, task):
2013 self.stats.taskCompleted()
2014 bb.event.fire(runQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
2015 self.task_completeoutright(task)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002016 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002017
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002018 def task_fail(self, task, exitcode, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002019 """
2020 Called when a task has failed
2021 Updates the state engine with the failure
2022 """
2023 self.stats.taskFailed()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002024 self.failed_tids.append(task)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002025
Andrew Geissler595f6302022-01-24 19:11:47 +00002026 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002027 if fakerootlog and os.path.exists(fakerootlog):
2028 with open(fakerootlog) as fakeroot_log_file:
2029 fakeroot_failed = False
2030 for line in reversed(fakeroot_log_file.readlines()):
2031 for fakeroot_error in ['mismatch', 'error', 'fatal']:
2032 if fakeroot_error in line.lower():
2033 fakeroot_failed = True
2034 if 'doing new pid setup and server start' in line:
2035 break
Andrew Geissler595f6302022-01-24 19:11:47 +00002036 fakeroot_log.append(line)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002037
2038 if not fakeroot_failed:
Andrew Geissler595f6302022-01-24 19:11:47 +00002039 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002040
Andrew Geissler595f6302022-01-24 19:11:47 +00002041 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 -05002042
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002043 if self.rqdata.taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002044 self.rq.state = runQueueCleanUp
2045
2046 def task_skip(self, task, reason):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002047 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002048 self.setbuildable(task)
2049 bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
2050 self.task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002051 self.stats.taskSkipped()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002052 self.stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002053
Brad Bishop08902b02019-08-20 09:16:51 -04002054 def summarise_scenequeue_errors(self):
2055 err = False
2056 if not self.sqdone:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002057 logger.debug('We could skip tasks %s', "\n".join(sorted(self.scenequeue_covered)))
Andrew Geissler5199d832021-09-24 16:47:35 -05002058 completeevent = sceneQueueComplete(self.stats, self.rq)
Brad Bishop08902b02019-08-20 09:16:51 -04002059 bb.event.fire(completeevent, self.cfgData)
2060 if self.sq_deferred:
2061 logger.error("Scenequeue had deferred entries: %s" % pprint.pformat(self.sq_deferred))
2062 err = True
2063 if self.updated_taskhash_queue:
2064 logger.error("Scenequeue had unprocessed changed taskhash entries: %s" % pprint.pformat(self.updated_taskhash_queue))
2065 err = True
2066 if self.holdoff_tasks:
2067 logger.error("Scenequeue had holdoff tasks: %s" % pprint.pformat(self.holdoff_tasks))
2068 err = True
2069
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002070 for tid in self.scenequeue_covered.intersection(self.scenequeue_notcovered):
2071 # No task should end up in both covered and uncovered, that is a bug.
2072 logger.error("Setscene task %s in both covered and notcovered." % tid)
2073
Brad Bishop08902b02019-08-20 09:16:51 -04002074 for tid in self.rqdata.runq_setscene_tids:
2075 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2076 err = True
2077 logger.error("Setscene Task %s was never marked as covered or not covered" % tid)
2078 if tid not in self.sq_buildable:
2079 err = True
2080 logger.error("Setscene Task %s was never marked as buildable" % tid)
2081 if tid not in self.sq_running:
2082 err = True
2083 logger.error("Setscene Task %s was never marked as running" % tid)
2084
2085 for x in self.rqdata.runtaskentries:
2086 if x not in self.tasks_covered and x not in self.tasks_notcovered:
2087 logger.error("Task %s was never moved from the setscene queue" % x)
2088 err = True
2089 if x not in self.tasks_scenequeue_done:
2090 logger.error("Task %s was never processed by the setscene code" % x)
2091 err = True
Andrew Geissler595f6302022-01-24 19:11:47 +00002092 if not self.rqdata.runtaskentries[x].depends and x not in self.runq_buildable:
Brad Bishop08902b02019-08-20 09:16:51 -04002093 logger.error("Task %s was never marked as buildable by the setscene code" % x)
2094 err = True
2095 return err
2096
2097
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002098 def execute(self):
2099 """
Brad Bishop96ff1982019-08-19 13:50:42 -04002100 Run the tasks in a queue prepared by prepare_runqueue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002101 """
2102
2103 self.rq.read_workers()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002104 if self.updated_taskhash_queue or self.pending_migrations:
2105 self.process_possible_migrations()
2106
2107 if not hasattr(self, "sorted_setscene_tids"):
2108 # Don't want to sort this set every execution
2109 self.sorted_setscene_tids = sorted(self.rqdata.runq_setscene_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002110
Brad Bishop96ff1982019-08-19 13:50:42 -04002111 task = None
2112 if not self.sqdone and self.can_start_task():
2113 # Find the next setscene to run
Andrew Geissler82c905d2020-04-13 13:39:40 -05002114 for nexttask in self.sorted_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002115 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 +00002116 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 -04002117 if nexttask not in self.rqdata.target_tids:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002118 logger.debug2("Skipping setscene for task %s" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002119 self.sq_task_skip(nexttask)
2120 self.scenequeue_notneeded.add(nexttask)
2121 if nexttask in self.sq_deferred:
2122 del self.sq_deferred[nexttask]
2123 return True
Brad Bishop08902b02019-08-20 09:16:51 -04002124 # If covered tasks are running, need to wait for them to complete
2125 for t in self.sqdata.sq_covered_tasks[nexttask]:
2126 if t in self.runq_running and t not in self.runq_complete:
2127 continue
Brad Bishop96ff1982019-08-19 13:50:42 -04002128 if nexttask in self.sq_deferred:
2129 if self.sq_deferred[nexttask] not in self.runq_complete:
2130 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002131 logger.debug("Task %s no longer deferred" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002132 del self.sq_deferred[nexttask]
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002133 valid = self.rq.validate_hashes(set([nexttask]), self.cooker.data, 0, False, summary=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002134 if not valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002135 logger.debug("%s didn't become valid, skipping setscene" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002136 self.sq_task_failoutright(nexttask)
2137 return True
Brad Bishop96ff1982019-08-19 13:50:42 -04002138 if nexttask in self.sqdata.outrightfail:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002139 logger.debug2('No package found, so skipping setscene task %s', nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002140 self.sq_task_failoutright(nexttask)
2141 return True
2142 if nexttask in self.sqdata.unskippable:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002143 logger.debug2("Setscene task %s is unskippable" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002144 task = nexttask
2145 break
2146 if task is not None:
2147 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
2148 taskname = taskname + "_setscene"
2149 if self.rq.check_stamp_task(task, taskname_from_tid(task), recurse = True, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002150 logger.debug2('Stamp for underlying task %s is current, so skipping setscene variant', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002151 self.sq_task_failoutright(task)
2152 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002153
Brad Bishop96ff1982019-08-19 13:50:42 -04002154 if self.cooker.configuration.force:
2155 if task in self.rqdata.target_tids:
2156 self.sq_task_failoutright(task)
2157 return True
2158
2159 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002160 logger.debug2('Setscene stamp current task %s, so skip it and its dependencies', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002161 self.sq_task_skip(task)
2162 return True
2163
2164 if self.cooker.configuration.skipsetscene:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002165 logger.debug2('No setscene tasks should be executed. Skipping %s', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002166 self.sq_task_failoutright(task)
2167 return True
2168
Andrew Geissler5199d832021-09-24 16:47:35 -05002169 startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
Brad Bishop96ff1982019-08-19 13:50:42 -04002170 bb.event.fire(startevent, self.cfgData)
2171
Brad Bishop96ff1982019-08-19 13:50:42 -04002172 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002173 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002174 runtask = {
2175 'fn' : taskfn,
2176 'task' : task,
2177 'taskname' : taskname,
2178 'taskhash' : self.rqdata.get_task_hash(task),
2179 'unihash' : self.rqdata.get_task_unihash(task),
2180 'quieterrors' : True,
2181 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002182 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002183 'taskdepdata' : self.sq_build_taskdepdata(task),
2184 'dry_run' : False,
2185 'taskdep': taskdep,
2186 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2187 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2188 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2189 }
2190
Brad Bishop96ff1982019-08-19 13:50:42 -04002191 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
2192 if not mc in self.rq.fakeworker:
2193 self.rq.start_fakeworker(self, mc)
Andrew Geissler517393d2023-01-13 08:55:19 -06002194 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002195 self.rq.fakeworker[mc].process.stdin.flush()
2196 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06002197 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002198 self.rq.worker[mc].process.stdin.flush()
2199
Andrew Geissler517393d2023-01-13 08:55:19 -06002200 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002201 self.build_stamps2.append(self.build_stamps[task])
2202 self.sq_running.add(task)
2203 self.sq_live.add(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05002204 self.stats.updateActiveSetscene(len(self.sq_live))
Brad Bishop96ff1982019-08-19 13:50:42 -04002205 if self.can_start_task():
2206 return True
2207
Brad Bishopc68388fc2019-08-26 01:33:31 -04002208 self.update_holdofftasks()
2209
Brad Bishop08902b02019-08-20 09:16:51 -04002210 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 -05002211 hashequiv_logger.verbose("Setscene tasks completed")
Brad Bishop96ff1982019-08-19 13:50:42 -04002212
Brad Bishop08902b02019-08-20 09:16:51 -04002213 err = self.summarise_scenequeue_errors()
Brad Bishop96ff1982019-08-19 13:50:42 -04002214 if err:
2215 self.rq.state = runQueueFailed
2216 return True
2217
2218 if self.cooker.configuration.setsceneonly:
2219 self.rq.state = runQueueComplete
2220 return True
2221 self.sqdone = True
2222
2223 if self.stats.total == 0:
2224 # nothing to do
2225 self.rq.state = runQueueComplete
2226 return True
2227
2228 if self.cooker.configuration.setsceneonly:
2229 task = None
2230 else:
2231 task = self.sched.next()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002232 if task is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002233 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002234
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002235 if self.rqdata.setscene_ignore_tasks is not None:
2236 if self.check_setscene_ignore_tasks(task):
2237 self.task_fail(task, "setscene ignore_tasks")
Brad Bishop96ff1982019-08-19 13:50:42 -04002238 return True
2239
2240 if task in self.tasks_covered:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002241 logger.debug2("Setscene covered task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002242 self.task_skip(task, "covered")
2243 return True
2244
2245 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002246 logger.debug2("Stamp current task %s", task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002247
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002248 self.task_skip(task, "existing")
Andrew Geissler82c905d2020-04-13 13:39:40 -05002249 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002250 return True
2251
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002252 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002253 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2254 startevent = runQueueTaskStarted(task, self.stats, self.rq,
2255 noexec=True)
2256 bb.event.fire(startevent, self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002257 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002258 self.stats.taskActive()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002259 if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Andrew Geissler517393d2023-01-13 08:55:19 -06002260 bb.build.make_stamp_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002261 self.task_complete(task)
2262 return True
2263 else:
2264 startevent = runQueueTaskStarted(task, self.stats, self.rq)
2265 bb.event.fire(startevent, self.cfgData)
2266
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002267 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002268 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002269 runtask = {
2270 'fn' : taskfn,
2271 'task' : task,
2272 'taskname' : taskname,
2273 'taskhash' : self.rqdata.get_task_hash(task),
2274 'unihash' : self.rqdata.get_task_unihash(task),
2275 'quieterrors' : False,
2276 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002277 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002278 'taskdepdata' : self.build_taskdepdata(task),
2279 'dry_run' : self.rqdata.setscene_enforce,
2280 'taskdep': taskdep,
2281 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2282 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2283 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2284 }
2285
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002286 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 -05002287 if not mc in self.rq.fakeworker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002288 try:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002289 self.rq.start_fakeworker(self, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002290 except OSError as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002291 logger.critical("Failed to spawn fakeroot worker to run %s: %s" % (task, str(exc)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002292 self.rq.state = runQueueFailed
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002293 self.stats.taskFailed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002294 return True
Andrew Geissler517393d2023-01-13 08:55:19 -06002295 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002296 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002297 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06002298 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002299 self.rq.worker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002300
Andrew Geissler517393d2023-01-13 08:55:19 -06002301 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002302 self.build_stamps2.append(self.build_stamps[task])
2303 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002304 self.stats.taskActive()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002305 if self.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002306 return True
2307
Andrew Geissler595f6302022-01-24 19:11:47 +00002308 if self.stats.active > 0 or self.sq_live:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002309 self.rq.read_workers()
2310 return self.rq.active_fds()
2311
Brad Bishop96ff1982019-08-19 13:50:42 -04002312 # No more tasks can be run. If we have deferred setscene tasks we should run them.
2313 if self.sq_deferred:
Patrick Williams92b42cb2022-09-03 06:53:57 -05002314 deferred_tid = list(self.sq_deferred.keys())[0]
2315 blocking_tid = self.sq_deferred.pop(deferred_tid)
Patrick Williams2390b1b2022-11-03 13:47:49 -05002316 logger.warning("Runqueue deadlocked on deferred tasks, forcing task %s blocked by %s" % (deferred_tid, blocking_tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04002317 return True
2318
Andrew Geissler595f6302022-01-24 19:11:47 +00002319 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002320 self.rq.state = runQueueFailed
2321 return True
2322
2323 # Sanity Checks
Brad Bishop08902b02019-08-20 09:16:51 -04002324 err = self.summarise_scenequeue_errors()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002325 for task in self.rqdata.runtaskentries:
2326 if task not in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002327 logger.error("Task %s never buildable!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002328 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002329 elif task not in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002330 logger.error("Task %s never ran!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002331 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002332 elif task not in self.runq_complete:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002333 logger.error("Task %s never completed!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002334 err = True
2335
2336 if err:
2337 self.rq.state = runQueueFailed
2338 else:
2339 self.rq.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002340
2341 return True
2342
Brad Bishopc68388fc2019-08-26 01:33:31 -04002343 def filtermcdeps(self, task, mc, deps):
Andrew Geissler99467da2019-02-25 18:54:23 -06002344 ret = set()
Andrew Geissler99467da2019-02-25 18:54:23 -06002345 for dep in deps:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002346 thismc = mc_from_tid(dep)
2347 if thismc != mc:
Andrew Geissler99467da2019-02-25 18:54:23 -06002348 continue
2349 ret.add(dep)
2350 return ret
2351
Brad Bishopa34c0302019-09-23 22:34:48 -04002352 # We filter out multiconfig dependencies from taskdepdata we pass to the tasks
Andrew Geissler99467da2019-02-25 18:54:23 -06002353 # as most code can't handle them
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002354 def build_taskdepdata(self, task):
2355 taskdepdata = {}
Brad Bishopc68388fc2019-08-26 01:33:31 -04002356 mc = mc_from_tid(task)
Brad Bishop08902b02019-08-20 09:16:51 -04002357 next = self.rqdata.runtaskentries[task].depends.copy()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002358 next.add(task)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002359 next = self.filtermcdeps(task, mc, next)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002360 while next:
2361 additional = []
2362 for revdep in next:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002363 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2364 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2365 deps = self.rqdata.runtaskentries[revdep].depends
2366 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002367 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002368 unihash = self.rqdata.runtaskentries[revdep].unihash
Brad Bishopc68388fc2019-08-26 01:33:31 -04002369 deps = self.filtermcdeps(task, mc, deps)
Patrick Williamsb542dec2023-06-09 01:26:37 -05002370 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2371 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002372 for revdep2 in deps:
2373 if revdep2 not in taskdepdata:
2374 additional.append(revdep2)
2375 next = additional
2376
2377 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2378 return taskdepdata
2379
Brad Bishop08902b02019-08-20 09:16:51 -04002380 def update_holdofftasks(self):
Brad Bishopc68388fc2019-08-26 01:33:31 -04002381
2382 if not self.holdoff_need_update:
2383 return
2384
2385 notcovered = set(self.scenequeue_notcovered)
2386 notcovered |= self.cantskip
2387 for tid in self.scenequeue_notcovered:
2388 notcovered |= self.sqdata.sq_covered_tasks[tid]
2389 notcovered |= self.sqdata.unskippable.difference(self.rqdata.runq_setscene_tids)
2390 notcovered.intersection_update(self.tasks_scenequeue_done)
2391
2392 covered = set(self.scenequeue_covered)
2393 for tid in self.scenequeue_covered:
2394 covered |= self.sqdata.sq_covered_tasks[tid]
2395 covered.difference_update(notcovered)
2396 covered.intersection_update(self.tasks_scenequeue_done)
2397
2398 for tid in notcovered | covered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002399 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002400 self.setbuildable(tid)
2401 elif self.rqdata.runtaskentries[tid].depends.issubset(self.runq_complete):
2402 self.setbuildable(tid)
2403
2404 self.tasks_covered = covered
2405 self.tasks_notcovered = notcovered
2406
Brad Bishop08902b02019-08-20 09:16:51 -04002407 self.holdoff_tasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002408
Brad Bishop08902b02019-08-20 09:16:51 -04002409 for tid in self.rqdata.runq_setscene_tids:
2410 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2411 self.holdoff_tasks.add(tid)
2412
2413 for tid in self.holdoff_tasks.copy():
2414 for dep in self.sqdata.sq_covered_tasks[tid]:
2415 if dep not in self.runq_complete:
2416 self.holdoff_tasks.add(dep)
2417
Brad Bishopc68388fc2019-08-26 01:33:31 -04002418 self.holdoff_need_update = False
2419
Brad Bishop08902b02019-08-20 09:16:51 -04002420 def process_possible_migrations(self):
2421
2422 changed = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002423 toprocess = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002424 for tid, unihash in self.updated_taskhash_queue.copy():
2425 if tid in self.runq_running and tid not in self.runq_complete:
2426 continue
2427
2428 self.updated_taskhash_queue.remove((tid, unihash))
2429
2430 if unihash != self.rqdata.runtaskentries[tid].unihash:
Andrew Geisslerc926e172021-05-07 16:11:35 -05002431 # Make sure we rehash any other tasks with the same task hash that we're deferred against.
2432 torehash = [tid]
2433 for deftid in self.sq_deferred:
2434 if self.sq_deferred[deftid] == tid:
2435 torehash.append(deftid)
2436 for hashtid in torehash:
2437 hashequiv_logger.verbose("Task %s unihash changed to %s" % (hashtid, unihash))
2438 self.rqdata.runtaskentries[hashtid].unihash = unihash
2439 bb.parse.siggen.set_unihash(hashtid, unihash)
2440 toprocess.add(hashtid)
Andrew Geissler78b72792022-06-14 06:47:25 -05002441 if torehash:
2442 # Need to save after set_unihash above
2443 bb.parse.siggen.save_unitaskhashes()
Brad Bishop08902b02019-08-20 09:16:51 -04002444
Andrew Geissler82c905d2020-04-13 13:39:40 -05002445 # Work out all tasks which depend upon these
2446 total = set()
2447 next = set()
2448 for p in toprocess:
2449 next |= self.rqdata.runtaskentries[p].revdeps
2450 while next:
2451 current = next.copy()
2452 total = total | next
2453 next = set()
2454 for ntid in current:
2455 next |= self.rqdata.runtaskentries[ntid].revdeps
2456 next.difference_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002457
Andrew Geissler82c905d2020-04-13 13:39:40 -05002458 # Now iterate those tasks in dependency order to regenerate their taskhash/unihash
2459 next = set()
2460 for p in total:
Andrew Geissler595f6302022-01-24 19:11:47 +00002461 if not self.rqdata.runtaskentries[p].depends:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002462 next.add(p)
2463 elif self.rqdata.runtaskentries[p].depends.isdisjoint(total):
2464 next.add(p)
2465
2466 # When an item doesn't have dependencies in total, we can process it. Drop items from total when handled
2467 while next:
2468 current = next.copy()
2469 next = set()
2470 for tid in current:
Andrew Geissler595f6302022-01-24 19:11:47 +00002471 if self.rqdata.runtaskentries[p].depends and not self.rqdata.runtaskentries[tid].depends.isdisjoint(total):
Andrew Geissler82c905d2020-04-13 13:39:40 -05002472 continue
2473 orighash = self.rqdata.runtaskentries[tid].hash
Andrew Geissler517393d2023-01-13 08:55:19 -06002474 newhash = bb.parse.siggen.get_taskhash(tid, self.rqdata.runtaskentries[tid].depends, self.rqdata.dataCaches)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002475 origuni = self.rqdata.runtaskentries[tid].unihash
2476 newuni = bb.parse.siggen.get_unihash(tid)
2477 # FIXME, need to check it can come from sstate at all for determinism?
2478 remapped = False
2479 if newuni == origuni:
2480 # Nothing to do, we match, skip code below
2481 remapped = True
2482 elif tid in self.scenequeue_covered or tid in self.sq_live:
2483 # Already ran this setscene task or it running. Report the new taskhash
2484 bb.parse.siggen.report_unihash_equiv(tid, newhash, origuni, newuni, self.rqdata.dataCaches)
2485 hashequiv_logger.verbose("Already covered setscene for %s so ignoring rehash (remap)" % (tid))
2486 remapped = True
2487
2488 if not remapped:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002489 #logger.debug("Task %s hash changes: %s->%s %s->%s" % (tid, orighash, newhash, origuni, newuni))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002490 self.rqdata.runtaskentries[tid].hash = newhash
2491 self.rqdata.runtaskentries[tid].unihash = newuni
2492 changed.add(tid)
2493
2494 next |= self.rqdata.runtaskentries[tid].revdeps
2495 total.remove(tid)
2496 next.intersection_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002497
2498 if changed:
2499 for mc in self.rq.worker:
2500 self.rq.worker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2501 for mc in self.rq.fakeworker:
2502 self.rq.fakeworker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2503
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002504 hashequiv_logger.debug(pprint.pformat("Tasks changed:\n%s" % (changed)))
Brad Bishop08902b02019-08-20 09:16:51 -04002505
2506 for tid in changed:
2507 if tid not in self.rqdata.runq_setscene_tids:
2508 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002509 if tid not in self.pending_migrations:
2510 self.pending_migrations.add(tid)
2511
Andrew Geissler82c905d2020-04-13 13:39:40 -05002512 update_tasks = []
Brad Bishop08902b02019-08-20 09:16:51 -04002513 for tid in self.pending_migrations.copy():
Andrew Geissler82c905d2020-04-13 13:39:40 -05002514 if tid in self.runq_running or tid in self.sq_live:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002515 # Too late, task already running, not much we can do now
2516 self.pending_migrations.remove(tid)
2517 continue
2518
Brad Bishop08902b02019-08-20 09:16:51 -04002519 valid = True
2520 # Check no tasks this covers are running
2521 for dep in self.sqdata.sq_covered_tasks[tid]:
2522 if dep in self.runq_running and dep not in self.runq_complete:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002523 hashequiv_logger.debug2("Task %s is running which blocks setscene for %s from running" % (dep, tid))
Brad Bishop08902b02019-08-20 09:16:51 -04002524 valid = False
2525 break
2526 if not valid:
2527 continue
2528
2529 self.pending_migrations.remove(tid)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002530 changed = True
Brad Bishop08902b02019-08-20 09:16:51 -04002531
2532 if tid in self.tasks_scenequeue_done:
2533 self.tasks_scenequeue_done.remove(tid)
2534 for dep in self.sqdata.sq_covered_tasks[tid]:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002535 if dep in self.runq_complete and dep not in self.runq_tasksrun:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002536 bb.error("Task %s marked as completed but now needing to rerun? Halting build." % dep)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002537 self.failed_tids.append(tid)
2538 self.rq.state = runQueueCleanUp
2539 return
2540
Brad Bishop08902b02019-08-20 09:16:51 -04002541 if dep not in self.runq_complete:
2542 if dep in self.tasks_scenequeue_done and dep not in self.sqdata.unskippable:
2543 self.tasks_scenequeue_done.remove(dep)
2544
2545 if tid in self.sq_buildable:
2546 self.sq_buildable.remove(tid)
2547 if tid in self.sq_running:
2548 self.sq_running.remove(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002549 if tid in self.sqdata.outrightfail:
2550 self.sqdata.outrightfail.remove(tid)
2551 if tid in self.scenequeue_notcovered:
2552 self.scenequeue_notcovered.remove(tid)
2553 if tid in self.scenequeue_covered:
2554 self.scenequeue_covered.remove(tid)
2555 if tid in self.scenequeue_notneeded:
2556 self.scenequeue_notneeded.remove(tid)
2557
2558 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2559 self.sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2560
2561 if tid in self.stampcache:
2562 del self.stampcache[tid]
2563
2564 if tid in self.build_stamps:
2565 del self.build_stamps[tid]
2566
2567 update_tasks.append(tid)
2568
2569 update_tasks2 = []
2570 for tid in update_tasks:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002571 harddepfail = False
2572 for t in self.sqdata.sq_harddeps:
2573 if tid in self.sqdata.sq_harddeps[t] and t in self.scenequeue_notcovered:
2574 harddepfail = True
2575 break
2576 if not harddepfail and self.sqdata.sq_revdeps[tid].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
Brad Bishop08902b02019-08-20 09:16:51 -04002577 if tid not in self.sq_buildable:
2578 self.sq_buildable.add(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +00002579 if not self.sqdata.sq_revdeps[tid]:
Brad Bishop08902b02019-08-20 09:16:51 -04002580 self.sq_buildable.add(tid)
2581
Andrew Geissler517393d2023-01-13 08:55:19 -06002582 update_tasks2.append((tid, harddepfail, tid in self.sqdata.valid))
Brad Bishop08902b02019-08-20 09:16:51 -04002583
Andrew Geissler517393d2023-01-13 08:55:19 -06002584 if update_tasks2:
Brad Bishop08902b02019-08-20 09:16:51 -04002585 self.sqdone = False
Patrick Williams92b42cb2022-09-03 06:53:57 -05002586 for mc in sorted(self.sqdata.multiconfigs):
Andrew Geissler517393d2023-01-13 08:55:19 -06002587 for tid in sorted([t[0] for t in update_tasks2]):
Patrick Williams92b42cb2022-09-03 06:53:57 -05002588 if mc_from_tid(tid) != mc:
2589 continue
2590 h = pending_hash_index(tid, self.rqdata)
2591 if h in self.sqdata.hashes and tid != self.sqdata.hashes[h]:
2592 self.sq_deferred[tid] = self.sqdata.hashes[h]
2593 bb.note("Deferring %s after %s" % (tid, self.sqdata.hashes[h]))
Andrew Geissler517393d2023-01-13 08:55:19 -06002594 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 -05002595
Andrew Geissler517393d2023-01-13 08:55:19 -06002596 for (tid, harddepfail, origvalid) in update_tasks2:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002597 if tid in self.sqdata.valid and not origvalid:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002598 hashequiv_logger.verbose("Setscene task %s became valid" % tid)
2599 if harddepfail:
Andrew Geissler517393d2023-01-13 08:55:19 -06002600 logger.debug2("%s has an unavailable hard dependency so skipping" % (tid))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002601 self.sq_task_failoutright(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002602
2603 if changed:
Andrew Geissler5199d832021-09-24 16:47:35 -05002604 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002605 self.holdoff_need_update = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002606
Brad Bishop96ff1982019-08-19 13:50:42 -04002607 def scenequeue_updatecounters(self, task, fail=False):
Brad Bishop08902b02019-08-20 09:16:51 -04002608
2609 for dep in sorted(self.sqdata.sq_deps[task]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002610 if fail and task in self.sqdata.sq_harddeps and dep in self.sqdata.sq_harddeps[task]:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002611 if dep in self.scenequeue_covered or dep in self.scenequeue_notcovered:
2612 # dependency could be already processed, e.g. noexec setscene task
2613 continue
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002614 noexec, stamppresent = check_setscene_stamps(dep, self.rqdata, self.rq, self.stampcache)
2615 if noexec or stamppresent:
2616 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002617 logger.debug2("%s was unavailable and is a hard dependency of %s so skipping" % (task, dep))
Brad Bishop96ff1982019-08-19 13:50:42 -04002618 self.sq_task_failoutright(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002619 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002620 if self.sqdata.sq_revdeps[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2621 if dep not in self.sq_buildable:
2622 self.sq_buildable.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002623
Brad Bishop96ff1982019-08-19 13:50:42 -04002624 next = set([task])
2625 while next:
2626 new = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002627 for t in sorted(next):
Brad Bishop96ff1982019-08-19 13:50:42 -04002628 self.tasks_scenequeue_done.add(t)
2629 # Look down the dependency chain for non-setscene things which this task depends on
2630 # and mark as 'done'
2631 for dep in self.rqdata.runtaskentries[t].depends:
2632 if dep in self.rqdata.runq_setscene_tids or dep in self.tasks_scenequeue_done:
2633 continue
2634 if self.rqdata.runtaskentries[dep].revdeps.issubset(self.tasks_scenequeue_done):
2635 new.add(dep)
Brad Bishop96ff1982019-08-19 13:50:42 -04002636 next = new
2637
Andrew Geissler5199d832021-09-24 16:47:35 -05002638 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002639 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002640
2641 def sq_task_completeoutright(self, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002642 """
2643 Mark a task as completed
2644 Look at the reverse dependencies and mark any task with
2645 completed dependencies as buildable
2646 """
2647
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002648 logger.debug('Found task %s which could be accelerated', task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002649 self.scenequeue_covered.add(task)
2650 self.scenequeue_updatecounters(task)
2651
Brad Bishop96ff1982019-08-19 13:50:42 -04002652 def sq_check_taskfail(self, task):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002653 if self.rqdata.setscene_ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002654 realtask = task.split('_setscene')[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002655 (mc, fn, taskname, taskfn) = split_tid_mcfn(realtask)
2656 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002657 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002658 logger.error('Task %s.%s failed' % (pn, taskname + "_setscene"))
2659 self.rq.state = runQueueCleanUp
2660
Brad Bishop96ff1982019-08-19 13:50:42 -04002661 def sq_task_complete(self, task):
Andrew Geissler5199d832021-09-24 16:47:35 -05002662 bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
Brad Bishop96ff1982019-08-19 13:50:42 -04002663 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002664
Brad Bishop96ff1982019-08-19 13:50:42 -04002665 def sq_task_fail(self, task, result):
Andrew Geissler5199d832021-09-24 16:47:35 -05002666 bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002667 self.scenequeue_notcovered.add(task)
2668 self.scenequeue_updatecounters(task, True)
Brad Bishop96ff1982019-08-19 13:50:42 -04002669 self.sq_check_taskfail(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002670
Brad Bishop96ff1982019-08-19 13:50:42 -04002671 def sq_task_failoutright(self, task):
2672 self.sq_running.add(task)
2673 self.sq_buildable.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002674 self.scenequeue_notcovered.add(task)
2675 self.scenequeue_updatecounters(task, True)
2676
Brad Bishop96ff1982019-08-19 13:50:42 -04002677 def sq_task_skip(self, task):
2678 self.sq_running.add(task)
2679 self.sq_buildable.add(task)
2680 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002681
Brad Bishop96ff1982019-08-19 13:50:42 -04002682 def sq_build_taskdepdata(self, task):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002683 def getsetscenedeps(tid):
2684 deps = set()
2685 (mc, fn, taskname, _) = split_tid_mcfn(tid)
2686 realtid = tid + "_setscene"
2687 idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
2688 for (depname, idependtask) in idepends:
2689 if depname not in self.rqdata.taskData[mc].build_targets:
2690 continue
2691
2692 depfn = self.rqdata.taskData[mc].build_targets[depname][0]
2693 if depfn is None:
2694 continue
2695 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2696 deps.add(deptid)
2697 return deps
2698
2699 taskdepdata = {}
2700 next = getsetscenedeps(task)
2701 next.add(task)
2702 while next:
2703 additional = []
2704 for revdep in next:
2705 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2706 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2707 deps = getsetscenedeps(revdep)
2708 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2709 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002710 unihash = self.rqdata.runtaskentries[revdep].unihash
Patrick Williamsb542dec2023-06-09 01:26:37 -05002711 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2712 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002713 for revdep2 in deps:
2714 if revdep2 not in taskdepdata:
2715 additional.append(revdep2)
2716 next = additional
2717
2718 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2719 return taskdepdata
2720
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002721 def check_setscene_ignore_tasks(self, tid):
2722 # Check task that is going to run against the ignore tasks list
Brad Bishop96ff1982019-08-19 13:50:42 -04002723 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2724 # Ignore covered tasks
2725 if tid in self.tasks_covered:
2726 return False
2727 # Ignore stamped tasks
2728 if self.rq.check_stamp_task(tid, taskname, cache=self.stampcache):
2729 return False
2730 # Ignore noexec tasks
2731 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2732 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2733 return False
2734
2735 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002736 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Brad Bishop96ff1982019-08-19 13:50:42 -04002737 if tid in self.rqdata.runq_setscene_tids:
Andrew Geissler595f6302022-01-24 19:11:47 +00002738 msg = ['Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)]
Brad Bishop96ff1982019-08-19 13:50:42 -04002739 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002740 msg = ['Task %s.%s attempted to execute unexpectedly' % (pn, taskname)]
Andrew Geissler82c905d2020-04-13 13:39:40 -05002741 for t in self.scenequeue_notcovered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002742 msg.append("\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash))
2743 msg.append('\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered))
2744 logger.error("".join(msg))
Brad Bishop96ff1982019-08-19 13:50:42 -04002745 return True
2746 return False
2747
2748class SQData(object):
2749 def __init__(self):
2750 # SceneQueue dependencies
2751 self.sq_deps = {}
2752 # SceneQueue reverse dependencies
2753 self.sq_revdeps = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002754 # Injected inter-setscene task dependencies
2755 self.sq_harddeps = {}
2756 # Cache of stamp files so duplicates can't run in parallel
2757 self.stamps = {}
2758 # Setscene tasks directly depended upon by the build
2759 self.unskippable = set()
2760 # List of setscene tasks which aren't present
2761 self.outrightfail = set()
2762 # A list of normal tasks a setscene task covers
2763 self.sq_covered_tasks = {}
2764
2765def build_scenequeue_data(sqdata, rqdata, rq, cooker, stampcache, sqrq):
2766
2767 sq_revdeps = {}
2768 sq_revdeps_squash = {}
2769 sq_collated_deps = {}
2770
2771 # We need to construct a dependency graph for the setscene functions. Intermediate
2772 # dependencies between the setscene tasks only complicate the code. This code
2773 # therefore aims to collapse the huge runqueue dependency tree into a smaller one
2774 # only containing the setscene functions.
2775
2776 rqdata.init_progress_reporter.next_stage()
2777
2778 # First process the chains up to the first setscene task.
2779 endpoints = {}
2780 for tid in rqdata.runtaskentries:
2781 sq_revdeps[tid] = copy.copy(rqdata.runtaskentries[tid].revdeps)
2782 sq_revdeps_squash[tid] = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00002783 if not sq_revdeps[tid] and tid not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002784 #bb.warn("Added endpoint %s" % (tid))
2785 endpoints[tid] = set()
2786
2787 rqdata.init_progress_reporter.next_stage()
2788
2789 # Secondly process the chains between setscene tasks.
2790 for tid in rqdata.runq_setscene_tids:
2791 sq_collated_deps[tid] = set()
2792 #bb.warn("Added endpoint 2 %s" % (tid))
2793 for dep in rqdata.runtaskentries[tid].depends:
2794 if tid in sq_revdeps[dep]:
2795 sq_revdeps[dep].remove(tid)
2796 if dep not in endpoints:
2797 endpoints[dep] = set()
2798 #bb.warn(" Added endpoint 3 %s" % (dep))
2799 endpoints[dep].add(tid)
2800
2801 rqdata.init_progress_reporter.next_stage()
2802
2803 def process_endpoints(endpoints):
2804 newendpoints = {}
2805 for point, task in endpoints.items():
2806 tasks = set()
2807 if task:
2808 tasks |= task
2809 if sq_revdeps_squash[point]:
2810 tasks |= sq_revdeps_squash[point]
2811 if point not in rqdata.runq_setscene_tids:
2812 for t in tasks:
2813 sq_collated_deps[t].add(point)
2814 sq_revdeps_squash[point] = set()
2815 if point in rqdata.runq_setscene_tids:
2816 sq_revdeps_squash[point] = tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002817 continue
2818 for dep in rqdata.runtaskentries[point].depends:
2819 if point in sq_revdeps[dep]:
2820 sq_revdeps[dep].remove(point)
2821 if tasks:
2822 sq_revdeps_squash[dep] |= tasks
Andrew Geissler595f6302022-01-24 19:11:47 +00002823 if not sq_revdeps[dep] and dep not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002824 newendpoints[dep] = task
Andrew Geissler595f6302022-01-24 19:11:47 +00002825 if newendpoints:
Brad Bishop96ff1982019-08-19 13:50:42 -04002826 process_endpoints(newendpoints)
2827
2828 process_endpoints(endpoints)
2829
2830 rqdata.init_progress_reporter.next_stage()
2831
Brad Bishop08902b02019-08-20 09:16:51 -04002832 # Build a list of tasks which are "unskippable"
2833 # These are direct endpoints referenced by the build upto and including setscene tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002834 # Take the build endpoints (no revdeps) and find the sstate tasks they depend upon
2835 new = True
2836 for tid in rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00002837 if not rqdata.runtaskentries[tid].revdeps:
Brad Bishop96ff1982019-08-19 13:50:42 -04002838 sqdata.unskippable.add(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002839 sqdata.unskippable |= sqrq.cantskip
Brad Bishop96ff1982019-08-19 13:50:42 -04002840 while new:
2841 new = False
Brad Bishop08902b02019-08-20 09:16:51 -04002842 orig = sqdata.unskippable.copy()
2843 for tid in sorted(orig, reverse=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04002844 if tid in rqdata.runq_setscene_tids:
2845 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00002846 if not rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04002847 # These are tasks which have no setscene tasks in their chain, need to mark as directly buildable
Brad Bishop96ff1982019-08-19 13:50:42 -04002848 sqrq.setbuildable(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002849 sqdata.unskippable |= rqdata.runtaskentries[tid].depends
Brad Bishop08902b02019-08-20 09:16:51 -04002850 if sqdata.unskippable != orig:
2851 new = True
2852
2853 sqrq.tasks_scenequeue_done |= sqdata.unskippable.difference(rqdata.runq_setscene_tids)
Brad Bishop96ff1982019-08-19 13:50:42 -04002854
2855 rqdata.init_progress_reporter.next_stage(len(rqdata.runtaskentries))
2856
2857 # Sanity check all dependencies could be changed to setscene task references
2858 for taskcounter, tid in enumerate(rqdata.runtaskentries):
2859 if tid in rqdata.runq_setscene_tids:
2860 pass
Andrew Geissler595f6302022-01-24 19:11:47 +00002861 elif sq_revdeps_squash[tid]:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002862 bb.msg.fatal("RunQueue", "Something went badly wrong during scenequeue generation, halting. Please report this problem.")
Brad Bishop96ff1982019-08-19 13:50:42 -04002863 else:
2864 del sq_revdeps_squash[tid]
2865 rqdata.init_progress_reporter.update(taskcounter)
2866
2867 rqdata.init_progress_reporter.next_stage()
2868
2869 # Resolve setscene inter-task dependencies
2870 # e.g. do_sometask_setscene[depends] = "targetname:do_someothertask_setscene"
2871 # Note that anything explicitly depended upon will have its reverse dependencies removed to avoid circular dependencies
2872 for tid in rqdata.runq_setscene_tids:
2873 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2874 realtid = tid + "_setscene"
2875 idepends = rqdata.taskData[mc].taskentries[realtid].idepends
Andrew Geissler517393d2023-01-13 08:55:19 -06002876 sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2877
Brad Bishop96ff1982019-08-19 13:50:42 -04002878 for (depname, idependtask) in idepends:
2879
2880 if depname not in rqdata.taskData[mc].build_targets:
2881 continue
2882
2883 depfn = rqdata.taskData[mc].build_targets[depname][0]
2884 if depfn is None:
2885 continue
2886 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2887 if deptid not in rqdata.runtaskentries:
2888 bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (realtid, depfn, idependtask))
2889
2890 if not deptid in sqdata.sq_harddeps:
2891 sqdata.sq_harddeps[deptid] = set()
2892 sqdata.sq_harddeps[deptid].add(tid)
2893
2894 sq_revdeps_squash[tid].add(deptid)
2895 # Have to zero this to avoid circular dependencies
2896 sq_revdeps_squash[deptid] = set()
2897
2898 rqdata.init_progress_reporter.next_stage()
2899
2900 for task in sqdata.sq_harddeps:
2901 for dep in sqdata.sq_harddeps[task]:
2902 sq_revdeps_squash[dep].add(task)
2903
2904 rqdata.init_progress_reporter.next_stage()
2905
2906 #for tid in sq_revdeps_squash:
2907 # data = ""
2908 # for dep in sq_revdeps_squash[tid]:
2909 # data = data + "\n %s" % dep
2910 # bb.warn("Task %s_setscene: is %s " % (tid, data))
2911
2912 sqdata.sq_revdeps = sq_revdeps_squash
Brad Bishop96ff1982019-08-19 13:50:42 -04002913 sqdata.sq_covered_tasks = sq_collated_deps
2914
2915 # Build reverse version of revdeps to populate deps structure
2916 for tid in sqdata.sq_revdeps:
2917 sqdata.sq_deps[tid] = set()
2918 for tid in sqdata.sq_revdeps:
2919 for dep in sqdata.sq_revdeps[tid]:
2920 sqdata.sq_deps[dep].add(tid)
2921
2922 rqdata.init_progress_reporter.next_stage()
2923
Brad Bishop00e122a2019-10-05 11:10:57 -04002924 sqdata.multiconfigs = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002925 for tid in sqdata.sq_revdeps:
Brad Bishop00e122a2019-10-05 11:10:57 -04002926 sqdata.multiconfigs.add(mc_from_tid(tid))
Andrew Geissler595f6302022-01-24 19:11:47 +00002927 if not sqdata.sq_revdeps[tid]:
Brad Bishop96ff1982019-08-19 13:50:42 -04002928 sqrq.sq_buildable.add(tid)
2929
2930 rqdata.init_progress_reporter.finish()
2931
Brad Bishop00e122a2019-10-05 11:10:57 -04002932 sqdata.noexec = set()
2933 sqdata.stamppresent = set()
2934 sqdata.valid = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002935
Patrick Williams213cb262021-08-07 19:21:33 -05002936 sqdata.hashes = {}
2937 sqrq.sq_deferred = {}
2938 for mc in sorted(sqdata.multiconfigs):
2939 for tid in sorted(sqdata.sq_revdeps):
2940 if mc_from_tid(tid) != mc:
2941 continue
2942 h = pending_hash_index(tid, rqdata)
2943 if h not in sqdata.hashes:
2944 sqdata.hashes[h] = tid
2945 else:
2946 sqrq.sq_deferred[tid] = sqdata.hashes[h]
Andrew Geissler8f840682023-07-21 09:09:43 -05002947 bb.debug(1, "Deferring %s after %s" % (tid, sqdata.hashes[h]))
Patrick Williams213cb262021-08-07 19:21:33 -05002948
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002949 update_scenequeue_data(sqdata.sq_revdeps, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002950
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002951 # Compute a list of 'stale' sstate tasks where the current hash does not match the one
2952 # in any stamp files. Pass the list out to metadata as an event.
2953 found = {}
2954 for tid in rqdata.runq_setscene_tids:
2955 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002956 stamps = bb.build.find_stale_stamps(taskname, taskfn)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002957 if stamps:
2958 if mc not in found:
2959 found[mc] = {}
2960 found[mc][tid] = stamps
2961 for mc in found:
2962 event = bb.event.StaleSetSceneTasks(found[mc])
2963 bb.event.fire(event, cooker.databuilder.mcdata[mc])
2964
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002965def check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=False):
2966
2967 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2968
2969 taskdep = rqdata.dataCaches[mc].task_deps[taskfn]
2970
2971 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Andrew Geissler517393d2023-01-13 08:55:19 -06002972 bb.build.make_stamp_mcfn(taskname + "_setscene", taskfn)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002973 return True, False
2974
2975 if rq.check_stamp_task(tid, taskname + "_setscene", cache=stampcache):
2976 logger.debug2('Setscene stamp current for task %s', tid)
2977 return False, True
2978
2979 if rq.check_stamp_task(tid, taskname, recurse = True, cache=stampcache):
2980 logger.debug2('Normal stamp current for task %s', tid)
2981 return False, True
2982
2983 return False, False
2984
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002985def update_scenequeue_data(tids, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True):
Brad Bishop00e122a2019-10-05 11:10:57 -04002986
2987 tocheck = set()
2988
2989 for tid in sorted(tids):
2990 if tid in sqdata.stamppresent:
2991 sqdata.stamppresent.remove(tid)
2992 if tid in sqdata.valid:
2993 sqdata.valid.remove(tid)
Andrew Geisslerc926e172021-05-07 16:11:35 -05002994 if tid in sqdata.outrightfail:
2995 sqdata.outrightfail.remove(tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04002996
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002997 noexec, stamppresent = check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002998
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002999 if noexec:
Brad Bishop00e122a2019-10-05 11:10:57 -04003000 sqdata.noexec.add(tid)
3001 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003002 logger.debug2("%s is noexec so skipping setscene" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04003003 continue
3004
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003005 if stamppresent:
Brad Bishop00e122a2019-10-05 11:10:57 -04003006 sqdata.stamppresent.add(tid)
3007 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003008 logger.debug2("%s has a valid stamp, skipping" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04003009 continue
3010
3011 tocheck.add(tid)
3012
Brad Bishop1d80a2e2019-11-15 16:35:03 -05003013 sqdata.valid |= rq.validate_hashes(tocheck, cooker.data, len(sqdata.stamppresent), False, summary=summary)
Brad Bishop00e122a2019-10-05 11:10:57 -04003014
Patrick Williams213cb262021-08-07 19:21:33 -05003015 for tid in tids:
3016 if tid in sqdata.stamppresent:
3017 continue
3018 if tid in sqdata.valid:
3019 continue
3020 if tid in sqdata.noexec:
3021 continue
3022 if tid in sqrq.scenequeue_covered:
3023 continue
3024 if tid in sqrq.scenequeue_notcovered:
3025 continue
3026 if tid in sqrq.sq_deferred:
3027 continue
3028 sqdata.outrightfail.add(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003029 logger.debug2("%s already handled (fallthrough), skipping" % (tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04003030
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003031class TaskFailure(Exception):
3032 """
3033 Exception raised when a task in a runqueue fails
3034 """
3035 def __init__(self, x):
3036 self.args = x
3037
3038
3039class runQueueExitWait(bb.event.Event):
3040 """
3041 Event when waiting for task processes to exit
3042 """
3043
3044 def __init__(self, remain):
3045 self.remain = remain
3046 self.message = "Waiting for %s active tasks to finish" % remain
3047 bb.event.Event.__init__(self)
3048
3049class runQueueEvent(bb.event.Event):
3050 """
3051 Base runQueue event class
3052 """
3053 def __init__(self, task, stats, rq):
3054 self.taskid = task
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003055 self.taskstring = task
3056 self.taskname = taskname_from_tid(task)
3057 self.taskfile = fn_from_tid(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003058 self.taskhash = rq.rqdata.get_task_hash(task)
3059 self.stats = stats.copy()
3060 bb.event.Event.__init__(self)
3061
3062class sceneQueueEvent(runQueueEvent):
3063 """
3064 Base sceneQueue event class
3065 """
3066 def __init__(self, task, stats, rq, noexec=False):
3067 runQueueEvent.__init__(self, task, stats, rq)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003068 self.taskstring = task + "_setscene"
3069 self.taskname = taskname_from_tid(task) + "_setscene"
3070 self.taskfile = fn_from_tid(task)
3071 self.taskhash = rq.rqdata.get_task_hash(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003072
3073class runQueueTaskStarted(runQueueEvent):
3074 """
3075 Event notifying a task was started
3076 """
3077 def __init__(self, task, stats, rq, noexec=False):
3078 runQueueEvent.__init__(self, task, stats, rq)
3079 self.noexec = noexec
3080
3081class sceneQueueTaskStarted(sceneQueueEvent):
3082 """
3083 Event notifying a setscene task was started
3084 """
3085 def __init__(self, task, stats, rq, noexec=False):
3086 sceneQueueEvent.__init__(self, task, stats, rq)
3087 self.noexec = noexec
3088
3089class runQueueTaskFailed(runQueueEvent):
3090 """
3091 Event notifying a task failed
3092 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003093 def __init__(self, task, stats, exitcode, rq, fakeroot_log=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003094 runQueueEvent.__init__(self, task, stats, rq)
3095 self.exitcode = exitcode
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003096 self.fakeroot_log = fakeroot_log
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003097
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003098 def __str__(self):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003099 if self.fakeroot_log:
3100 return "Task (%s) failed with exit code '%s' \nPseudo log:\n%s" % (self.taskstring, self.exitcode, self.fakeroot_log)
3101 else:
3102 return "Task (%s) failed with exit code '%s'" % (self.taskstring, self.exitcode)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003103
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003104class sceneQueueTaskFailed(sceneQueueEvent):
3105 """
3106 Event notifying a setscene task failed
3107 """
3108 def __init__(self, task, stats, exitcode, rq):
3109 sceneQueueEvent.__init__(self, task, stats, rq)
3110 self.exitcode = exitcode
3111
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003112 def __str__(self):
3113 return "Setscene task (%s) failed with exit code '%s' - real task will be run instead" % (self.taskstring, self.exitcode)
3114
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003115class sceneQueueComplete(sceneQueueEvent):
3116 """
3117 Event when all the sceneQueue tasks are complete
3118 """
3119 def __init__(self, stats, rq):
3120 self.stats = stats.copy()
3121 bb.event.Event.__init__(self)
3122
3123class runQueueTaskCompleted(runQueueEvent):
3124 """
3125 Event notifying a task completed
3126 """
3127
3128class sceneQueueTaskCompleted(sceneQueueEvent):
3129 """
3130 Event notifying a setscene task completed
3131 """
3132
3133class runQueueTaskSkipped(runQueueEvent):
3134 """
3135 Event notifying a task was skipped
3136 """
3137 def __init__(self, task, stats, rq, reason):
3138 runQueueEvent.__init__(self, task, stats, rq)
3139 self.reason = reason
3140
Brad Bishop08902b02019-08-20 09:16:51 -04003141class taskUniHashUpdate(bb.event.Event):
3142 """
3143 Base runQueue event class
3144 """
3145 def __init__(self, task, unihash):
3146 self.taskid = task
3147 self.unihash = unihash
3148 bb.event.Event.__init__(self)
3149
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003150class runQueuePipe():
3151 """
3152 Abstraction for a pipe between a worker thread and the server
3153 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003154 def __init__(self, pipein, pipeout, d, rq, rqexec, fakerootlogs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003155 self.input = pipein
3156 if pipeout:
3157 pipeout.close()
3158 bb.utils.nonblockingfd(self.input)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003159 self.queue = b""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003160 self.d = d
3161 self.rq = rq
3162 self.rqexec = rqexec
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003163 self.fakerootlogs = fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003164
3165 def setrunqueueexec(self, rqexec):
3166 self.rqexec = rqexec
3167
3168 def read(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003169 for workers, name in [(self.rq.worker, "Worker"), (self.rq.fakeworker, "Fakeroot")]:
3170 for worker in workers.values():
3171 worker.process.poll()
3172 if worker.process.returncode is not None and not self.rq.teardown:
3173 bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, worker.process.pid, str(worker.process.returncode)))
3174 self.rq.finish_runqueue(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003175
3176 start = len(self.queue)
3177 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003178 self.queue = self.queue + (self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003179 except (OSError, IOError) as e:
3180 if e.errno != errno.EAGAIN:
3181 raise
3182 end = len(self.queue)
3183 found = True
Andrew Geissler595f6302022-01-24 19:11:47 +00003184 while found and self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003185 found = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003186 index = self.queue.find(b"</event>")
3187 while index != -1 and self.queue.startswith(b"<event>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003188 try:
3189 event = pickle.loads(self.queue[7:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003190 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
3191 if isinstance(e, pickle.UnpicklingError) and "truncated" in str(e):
3192 # The pickled data could contain "</event>" so search for the next occurance
3193 # unpickling again, this should be the only way an unpickle error could occur
3194 index = self.queue.find(b"</event>", index + 1)
3195 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003196 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
3197 bb.event.fire_from_worker(event, self.d)
Brad Bishop08902b02019-08-20 09:16:51 -04003198 if isinstance(event, taskUniHashUpdate):
3199 self.rqexec.updated_taskhash_queue.append((event.taskid, event.unihash))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003200 found = True
3201 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003202 index = self.queue.find(b"</event>")
3203 index = self.queue.find(b"</exitcode>")
3204 while index != -1 and self.queue.startswith(b"<exitcode>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003205 try:
3206 task, status = pickle.loads(self.queue[10:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003207 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003208 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003209 (_, _, _, taskfn) = split_tid_mcfn(task)
3210 fakerootlog = None
3211 if self.fakerootlogs and taskfn and taskfn in self.fakerootlogs:
3212 fakerootlog = self.fakerootlogs[taskfn]
3213 self.rqexec.runqueue_process_waitpid(task, status, fakerootlog=fakerootlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003214 found = True
3215 self.queue = self.queue[index+11:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003216 index = self.queue.find(b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003217 return (end > start)
3218
3219 def close(self):
3220 while self.read():
3221 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00003222 if self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003223 print("Warning, worker left partial message: %s" % self.queue)
3224 self.input.close()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003225
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003226def get_setscene_enforce_ignore_tasks(d, targets):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003227 if d.getVar('BB_SETSCENE_ENFORCE') != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003228 return None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003229 ignore_tasks = (d.getVar("BB_SETSCENE_ENFORCE_IGNORE_TASKS") or "").split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003230 outlist = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003231 for item in ignore_tasks[:]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003232 if item.startswith('%:'):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003233 for (mc, target, task, fn) in targets:
3234 outlist.append(target + ':' + item.split(':')[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003235 else:
3236 outlist.append(item)
3237 return outlist
3238
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003239def check_setscene_enforce_ignore_tasks(pn, taskname, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003240 import fnmatch
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003241 if ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003242 item = '%s:%s' % (pn, taskname)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003243 for ignore_tasks in ignore_tasks:
3244 if fnmatch.fnmatch(item, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003245 return True
3246 return False
3247 return True