blob: e629ab7e7b064927b8f75b09a110b0148063dd2a [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""
2BitBake 'RunQueue' implementation
3
4Handles preparation and execution of a queue of tasks
5"""
6
7# Copyright (C) 2006-2007 Richard Purdie
8#
Brad Bishopc342db32019-05-15 21:57:59 -04009# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011
12import copy
13import os
14import sys
Patrick Williamsc124f4f2015-09-15 14:41:29 -050015import stat
Patrick Williamsc124f4f2015-09-15 14:41:29 -050016import errno
17import logging
18import re
19import bb
Andrew Geissler82c905d2020-04-13 13:39:40 -050020from bb import msg, event
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021from bb import monitordisk
22import subprocess
Patrick Williamsc0f7c042017-02-23 20:41:17 -060023import pickle
Brad Bishop6e60e8b2018-02-01 10:27:11 -050024from multiprocessing import Process
Brad Bishop19323692019-04-05 15:28:33 -040025import shlex
Brad Bishop96ff1982019-08-19 13:50:42 -040026import pprint
Patrick Williamsdb4c27e2022-08-05 08:10:29 -050027import time
Patrick Williamsc124f4f2015-09-15 14:41:29 -050028
29bblogger = logging.getLogger("BitBake")
30logger = logging.getLogger("BitBake.RunQueue")
Andrew Geissler82c905d2020-04-13 13:39:40 -050031hashequiv_logger = logging.getLogger("BitBake.RunQueue.HashEquiv")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032
Brad Bishop19323692019-04-05 15:28:33 -040033__find_sha256__ = re.compile( r'(?i)(?<![a-z0-9])[a-f0-9]{64}(?![a-z0-9])' )
Patrick Williamsc124f4f2015-09-15 14:41:29 -050034
Patrick Williamsc0f7c042017-02-23 20:41:17 -060035def fn_from_tid(tid):
36 return tid.rsplit(":", 1)[0]
37
38def taskname_from_tid(tid):
39 return tid.rsplit(":", 1)[1]
40
Andrew Geissler99467da2019-02-25 18:54:23 -060041def mc_from_tid(tid):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060042 if tid.startswith('mc:') and tid.count(':') >= 2:
Andrew Geissler99467da2019-02-25 18:54:23 -060043 return tid.split(':')[1]
44 return ""
45
Patrick Williamsc0f7c042017-02-23 20:41:17 -060046def split_tid(tid):
47 (mc, fn, taskname, _) = split_tid_mcfn(tid)
48 return (mc, fn, taskname)
49
Andrew Geissler5a43b432020-06-13 10:46:56 -050050def split_mc(n):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060051 if n.startswith("mc:") and n.count(':') >= 2:
Andrew Geissler5a43b432020-06-13 10:46:56 -050052 _, mc, n = n.split(":", 2)
53 return (mc, n)
54 return ('', n)
55
Patrick Williamsc0f7c042017-02-23 20:41:17 -060056def split_tid_mcfn(tid):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060057 if tid.startswith('mc:') and tid.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060058 elems = tid.split(':')
59 mc = elems[1]
60 fn = ":".join(elems[2:-1])
61 taskname = elems[-1]
Brad Bishop15ae2502019-06-18 21:44:24 -040062 mcfn = "mc:" + mc + ":" + fn
Patrick Williamsc0f7c042017-02-23 20:41:17 -060063 else:
64 tid = tid.rsplit(":", 1)
65 mc = ""
66 fn = tid[0]
67 taskname = tid[1]
68 mcfn = fn
69
70 return (mc, fn, taskname, mcfn)
71
72def build_tid(mc, fn, taskname):
73 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -040074 return "mc:" + mc + ":" + fn + ":" + taskname
Patrick Williamsc0f7c042017-02-23 20:41:17 -060075 return fn + ":" + taskname
76
Brad Bishop96ff1982019-08-19 13:50:42 -040077# Index used to pair up potentially matching multiconfig tasks
78# We match on PN, taskname and hash being equal
79def pending_hash_index(tid, rqdata):
80 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
81 pn = rqdata.dataCaches[mc].pkg_fn[taskfn]
Brad Bishop00e122a2019-10-05 11:10:57 -040082 h = rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -040083 return pn + ":" + "taskname" + h
84
Patrick Williamsc124f4f2015-09-15 14:41:29 -050085class RunQueueStats:
86 """
87 Holds statistics on the tasks handled by the associated runQueue
88 """
Andrew Geissler5199d832021-09-24 16:47:35 -050089 def __init__(self, total, setscene_total):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090 self.completed = 0
91 self.skipped = 0
92 self.failed = 0
93 self.active = 0
Andrew Geissler5199d832021-09-24 16:47:35 -050094 self.setscene_active = 0
95 self.setscene_covered = 0
96 self.setscene_notcovered = 0
97 self.setscene_total = setscene_total
Patrick Williamsc124f4f2015-09-15 14:41:29 -050098 self.total = total
99
100 def copy(self):
Andrew Geissler5199d832021-09-24 16:47:35 -0500101 obj = self.__class__(self.total, self.setscene_total)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102 obj.__dict__.update(self.__dict__)
103 return obj
104
105 def taskFailed(self):
106 self.active = self.active - 1
107 self.failed = self.failed + 1
108
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800109 def taskCompleted(self):
110 self.active = self.active - 1
111 self.completed = self.completed + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500112
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800113 def taskSkipped(self):
114 self.active = self.active + 1
115 self.skipped = self.skipped + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500116
117 def taskActive(self):
118 self.active = self.active + 1
119
Andrew Geissler5199d832021-09-24 16:47:35 -0500120 def updateCovered(self, covered, notcovered):
121 self.setscene_covered = covered
122 self.setscene_notcovered = notcovered
123
124 def updateActiveSetscene(self, active):
125 self.setscene_active = active
126
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127# These values indicate the next step due to be run in the
128# runQueue state machine
129runQueuePrepare = 2
130runQueueSceneInit = 3
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131runQueueRunning = 6
132runQueueFailed = 7
133runQueueCleanUp = 8
134runQueueComplete = 9
135
136class RunQueueScheduler(object):
137 """
138 Control the order tasks are scheduled in.
139 """
140 name = "basic"
141
142 def __init__(self, runqueue, rqdata):
143 """
144 The default scheduler just returns the first buildable task (the
145 priority map is sorted by task number)
146 """
147 self.rq = runqueue
148 self.rqdata = rqdata
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600149 self.numTasks = len(self.rqdata.runtaskentries)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600151 self.prio_map = [self.rqdata.runtaskentries.keys()]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500152
Brad Bishop08902b02019-08-20 09:16:51 -0400153 self.buildable = set()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800154 self.skip_maxthread = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500155 self.stamps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600156 for tid in self.rqdata.runtaskentries:
157 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -0600158 self.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600159 if tid in self.rq.runq_buildable:
160 self.buildable.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500161
162 self.rev_prio_map = None
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500163 self.is_pressure_usable()
164
165 def is_pressure_usable(self):
166 """
167 If monitoring pressure, return True if pressure files can be open and read. For example
168 openSUSE /proc/pressure/* files have readable file permissions but when read the error EOPNOTSUPP (Operation not supported)
169 is returned.
170 """
Patrick Williams92b42cb2022-09-03 06:53:57 -0500171 if self.rq.max_cpu_pressure or self.rq.max_io_pressure or self.rq.max_memory_pressure:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500172 try:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500173 with open("/proc/pressure/cpu") as cpu_pressure_fds, \
174 open("/proc/pressure/io") as io_pressure_fds, \
175 open("/proc/pressure/memory") as memory_pressure_fds:
176
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500177 self.prev_cpu_pressure = cpu_pressure_fds.readline().split()[4].split("=")[1]
178 self.prev_io_pressure = io_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williams92b42cb2022-09-03 06:53:57 -0500179 self.prev_memory_pressure = memory_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500180 self.prev_pressure_time = time.time()
181 self.check_pressure = True
182 except:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500183 bb.note("The /proc/pressure files can't be read. Continuing build without monitoring pressure")
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500184 self.check_pressure = False
185 else:
186 self.check_pressure = False
187
188 def exceeds_max_pressure(self):
189 """
190 Monitor the difference in total pressure at least once per second, if
Patrick Williams92b42cb2022-09-03 06:53:57 -0500191 BB_PRESSURE_MAX_{CPU|IO|MEMORY} are set, return True if above threshold.
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500192 """
193 if self.check_pressure:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500194 with open("/proc/pressure/cpu") as cpu_pressure_fds, \
195 open("/proc/pressure/io") as io_pressure_fds, \
196 open("/proc/pressure/memory") as memory_pressure_fds:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500197 # extract "total" from /proc/pressure/{cpu|io}
198 curr_cpu_pressure = cpu_pressure_fds.readline().split()[4].split("=")[1]
199 curr_io_pressure = io_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williams92b42cb2022-09-03 06:53:57 -0500200 curr_memory_pressure = memory_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500201 exceeds_cpu_pressure = self.rq.max_cpu_pressure and (float(curr_cpu_pressure) - float(self.prev_cpu_pressure)) > self.rq.max_cpu_pressure
202 exceeds_io_pressure = self.rq.max_io_pressure and (float(curr_io_pressure) - float(self.prev_io_pressure)) > self.rq.max_io_pressure
Patrick Williams92b42cb2022-09-03 06:53:57 -0500203 exceeds_memory_pressure = self.rq.max_memory_pressure and (float(curr_memory_pressure) - float(self.prev_memory_pressure)) > self.rq.max_memory_pressure
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500204 now = time.time()
205 if now - self.prev_pressure_time > 1.0:
206 self.prev_cpu_pressure = curr_cpu_pressure
207 self.prev_io_pressure = curr_io_pressure
Patrick Williams92b42cb2022-09-03 06:53:57 -0500208 self.prev_memory_pressure = curr_memory_pressure
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500209 self.prev_pressure_time = now
Patrick Williams92b42cb2022-09-03 06:53:57 -0500210 return (exceeds_cpu_pressure or exceeds_io_pressure or exceeds_memory_pressure)
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500211 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500212
213 def next_buildable_task(self):
214 """
215 Return the id of the first task we find that is buildable
216 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500217 # Once tasks are running we don't need to worry about them again
218 self.buildable.difference_update(self.rq.runq_running)
Brad Bishop08902b02019-08-20 09:16:51 -0400219 buildable = set(self.buildable)
Brad Bishop08902b02019-08-20 09:16:51 -0400220 buildable.difference_update(self.rq.holdoff_tasks)
221 buildable.intersection_update(self.rq.tasks_covered | self.rq.tasks_notcovered)
Brad Bishop96ff1982019-08-19 13:50:42 -0400222 if not buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500223 return None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800224
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500225 # Bitbake requires that at least one task be active. Only check for pressure if
226 # this is the case, otherwise the pressure limitation could result in no tasks
227 # being active and no new tasks started thereby, at times, breaking the scheduler.
228 if self.rq.stats.active and self.exceeds_max_pressure():
229 return None
230
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800231 # Filter out tasks that have a max number of threads that have been exceeded
232 skip_buildable = {}
233 for running in self.rq.runq_running.difference(self.rq.runq_complete):
234 rtaskname = taskname_from_tid(running)
235 if rtaskname not in self.skip_maxthread:
236 self.skip_maxthread[rtaskname] = self.rq.cfgData.getVarFlag(rtaskname, "number_threads")
237 if not self.skip_maxthread[rtaskname]:
238 continue
239 if rtaskname in skip_buildable:
240 skip_buildable[rtaskname] += 1
241 else:
242 skip_buildable[rtaskname] = 1
243
Brad Bishop96ff1982019-08-19 13:50:42 -0400244 if len(buildable) == 1:
Brad Bishop08902b02019-08-20 09:16:51 -0400245 tid = buildable.pop()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800246 taskname = taskname_from_tid(tid)
247 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
248 return None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600249 stamp = self.stamps[tid]
250 if stamp not in self.rq.build_stamps.values():
251 return tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500252
253 if not self.rev_prio_map:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600254 self.rev_prio_map = {}
255 for tid in self.rqdata.runtaskentries:
256 self.rev_prio_map[tid] = self.prio_map.index(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500257
258 best = None
259 bestprio = None
Brad Bishop96ff1982019-08-19 13:50:42 -0400260 for tid in buildable:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800261 taskname = taskname_from_tid(tid)
262 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
263 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600264 prio = self.rev_prio_map[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500265 if bestprio is None or bestprio > prio:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600266 stamp = self.stamps[tid]
267 if stamp in self.rq.build_stamps.values():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500268 continue
269 bestprio = prio
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600270 best = tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271
272 return best
273
274 def next(self):
275 """
276 Return the id of the task we should build next
277 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800278 if self.rq.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500279 return self.next_buildable_task()
280
Brad Bishop316dfdd2018-06-25 12:45:53 -0400281 def newbuildable(self, task):
Brad Bishop08902b02019-08-20 09:16:51 -0400282 self.buildable.add(task)
283
284 def removebuildable(self, task):
285 self.buildable.remove(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500286
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500287 def describe_task(self, taskid):
288 result = 'ID %s' % taskid
289 if self.rev_prio_map:
290 result = result + (' pri %d' % self.rev_prio_map[taskid])
291 return result
292
293 def dump_prio(self, comment):
294 bb.debug(3, '%s (most important first):\n%s' %
295 (comment,
296 '\n'.join(['%d. %s' % (index + 1, self.describe_task(taskid)) for
297 index, taskid in enumerate(self.prio_map)])))
298
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500299class RunQueueSchedulerSpeed(RunQueueScheduler):
300 """
301 A scheduler optimised for speed. The priority map is sorted by task weight,
302 heavier weighted tasks (tasks needed by the most other tasks) are run first.
303 """
304 name = "speed"
305
306 def __init__(self, runqueue, rqdata):
307 """
308 The priority map is sorted by task weight.
309 """
310 RunQueueScheduler.__init__(self, runqueue, rqdata)
311
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600312 weights = {}
313 for tid in self.rqdata.runtaskentries:
314 weight = self.rqdata.runtaskentries[tid].weight
315 if not weight in weights:
316 weights[weight] = []
317 weights[weight].append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500318
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600319 self.prio_map = []
320 for weight in sorted(weights):
321 for w in weights[weight]:
322 self.prio_map.append(w)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500323
324 self.prio_map.reverse()
325
326class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed):
327 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500328 A scheduler optimised to complete .bb files as quickly as possible. The
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500329 priority map is sorted by task weight, but then reordered so once a given
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500330 .bb file starts to build, it's completed as quickly as possible by
331 running all tasks related to the same .bb file one after the after.
332 This works well where disk space is at a premium and classes like OE's
333 rm_work are in force.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500334 """
335 name = "completion"
336
337 def __init__(self, runqueue, rqdata):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500338 super(RunQueueSchedulerCompletion, self).__init__(runqueue, rqdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500339
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500340 # Extract list of tasks for each recipe, with tasks sorted
341 # ascending from "must run first" (typically do_fetch) to
342 # "runs last" (do_build). The speed scheduler prioritizes
343 # tasks that must run first before the ones that run later;
344 # this is what we depend on here.
345 task_lists = {}
346 for taskid in self.prio_map:
347 fn, taskname = taskid.rsplit(':', 1)
348 task_lists.setdefault(fn, []).append(taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500349
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500350 # Now unify the different task lists. The strategy is that
351 # common tasks get skipped and new ones get inserted after the
352 # preceeding common one(s) as they are found. Because task
353 # lists should differ only by their number of tasks, but not
354 # the ordering of the common tasks, this should result in a
355 # deterministic result that is a superset of the individual
356 # task ordering.
357 all_tasks = []
358 for recipe, new_tasks in task_lists.items():
359 index = 0
360 old_task = all_tasks[index] if index < len(all_tasks) else None
361 for new_task in new_tasks:
362 if old_task == new_task:
363 # Common task, skip it. This is the fast-path which
364 # avoids a full search.
365 index += 1
366 old_task = all_tasks[index] if index < len(all_tasks) else None
367 else:
368 try:
369 index = all_tasks.index(new_task)
370 # Already present, just not at the current
371 # place. We re-synchronized by changing the
372 # index so that it matches again. Now
373 # move on to the next existing task.
374 index += 1
375 old_task = all_tasks[index] if index < len(all_tasks) else None
376 except ValueError:
377 # Not present. Insert before old_task, which
378 # remains the same (but gets shifted back).
379 all_tasks.insert(index, new_task)
380 index += 1
381 bb.debug(3, 'merged task list: %s' % all_tasks)
382
383 # Now reverse the order so that tasks that finish the work on one
384 # recipe are considered more imporant (= come first). The ordering
385 # is now so that do_build is most important.
386 all_tasks.reverse()
387
388 # Group tasks of the same kind before tasks of less important
389 # kinds at the head of the queue (because earlier = lower
390 # priority number = runs earlier), while preserving the
391 # ordering by recipe. If recipe foo is more important than
392 # bar, then the goal is to work on foo's do_populate_sysroot
393 # before bar's do_populate_sysroot and on the more important
394 # tasks of foo before any of the less important tasks in any
395 # other recipe (if those other recipes are more important than
396 # foo).
397 #
398 # All of this only applies when tasks are runable. Explicit
399 # dependencies still override this ordering by priority.
400 #
401 # Here's an example why this priority re-ordering helps with
402 # minimizing disk usage. Consider a recipe foo with a higher
403 # priority than bar where foo DEPENDS on bar. Then the
404 # implicit rule (from base.bbclass) is that foo's do_configure
405 # depends on bar's do_populate_sysroot. This ensures that
406 # bar's do_populate_sysroot gets done first. Normally the
407 # tasks from foo would continue to run once that is done, and
408 # bar only gets completed and cleaned up later. By ordering
409 # bar's task that depend on bar's do_populate_sysroot before foo's
410 # do_configure, that problem gets avoided.
411 task_index = 0
412 self.dump_prio('original priorities')
413 for task in all_tasks:
414 for index in range(task_index, self.numTasks):
415 taskid = self.prio_map[index]
416 taskname = taskid.rsplit(':', 1)[1]
417 if taskname == task:
418 del self.prio_map[index]
419 self.prio_map.insert(task_index, taskid)
420 task_index += 1
421 self.dump_prio('completion priorities')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500422
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600423class RunTaskEntry(object):
424 def __init__(self):
425 self.depends = set()
426 self.revdeps = set()
427 self.hash = None
Brad Bishop19323692019-04-05 15:28:33 -0400428 self.unihash = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600429 self.task = None
430 self.weight = 1
431
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500432class RunQueueData:
433 """
434 BitBake Run Queue implementation
435 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600436 def __init__(self, rq, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500437 self.cooker = cooker
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600438 self.dataCaches = dataCaches
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500439 self.taskData = taskData
440 self.targets = targets
441 self.rq = rq
442 self.warn_multi_bb = False
443
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000444 self.multi_provider_allowed = (cfgData.getVar("BB_MULTI_PROVIDER_ALLOWED") or "").split()
445 self.setscene_ignore_tasks = get_setscene_enforce_ignore_tasks(cfgData, targets)
446 self.setscene_ignore_tasks_checked = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500447 self.setscene_enforce = (cfgData.getVar('BB_SETSCENE_ENFORCE') == "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600448 self.init_progress_reporter = bb.progress.DummyMultiStageProcessProgressReporter()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500449
450 self.reset()
451
452 def reset(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600453 self.runtaskentries = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500454
455 def runq_depends_names(self, ids):
456 import re
457 ret = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600458 for id in ids:
459 nam = os.path.basename(id)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500460 nam = re.sub("_[^,]*,", ",", nam)
461 ret.extend([nam])
462 return ret
463
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600464 def get_task_hash(self, tid):
465 return self.runtaskentries[tid].hash
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500466
Brad Bishop19323692019-04-05 15:28:33 -0400467 def get_task_unihash(self, tid):
468 return self.runtaskentries[tid].unihash
469
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600470 def get_user_idstring(self, tid, task_name_suffix = ""):
471 return tid + task_name_suffix
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500472
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500473 def get_short_user_idstring(self, task, task_name_suffix = ""):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500474 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
475 pn = self.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600476 taskname = taskname_from_tid(task) + task_name_suffix
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500477 return "%s:%s" % (pn, taskname)
478
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500479 def circular_depchains_handler(self, tasks):
480 """
481 Some tasks aren't buildable, likely due to circular dependency issues.
482 Identify the circular dependencies and print them in a user readable format.
483 """
484 from copy import deepcopy
485
486 valid_chains = []
487 explored_deps = {}
488 msgs = []
489
Andrew Geissler99467da2019-02-25 18:54:23 -0600490 class TooManyLoops(Exception):
491 pass
492
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500493 def chain_reorder(chain):
494 """
495 Reorder a dependency chain so the lowest task id is first
496 """
497 lowest = 0
498 new_chain = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600499 for entry in range(len(chain)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500500 if chain[entry] < chain[lowest]:
501 lowest = entry
502 new_chain.extend(chain[lowest:])
503 new_chain.extend(chain[:lowest])
504 return new_chain
505
506 def chain_compare_equal(chain1, chain2):
507 """
508 Compare two dependency chains and see if they're the same
509 """
510 if len(chain1) != len(chain2):
511 return False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600512 for index in range(len(chain1)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500513 if chain1[index] != chain2[index]:
514 return False
515 return True
516
517 def chain_array_contains(chain, chain_array):
518 """
519 Return True if chain_array contains chain
520 """
521 for ch in chain_array:
522 if chain_compare_equal(ch, chain):
523 return True
524 return False
525
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600526 def find_chains(tid, prev_chain):
527 prev_chain.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500528 total_deps = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600529 total_deps.extend(self.runtaskentries[tid].revdeps)
530 for revdep in self.runtaskentries[tid].revdeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500531 if revdep in prev_chain:
532 idx = prev_chain.index(revdep)
533 # To prevent duplicates, reorder the chain to start with the lowest taskid
534 # and search through an array of those we've already printed
535 chain = prev_chain[idx:]
536 new_chain = chain_reorder(chain)
537 if not chain_array_contains(new_chain, valid_chains):
538 valid_chains.append(new_chain)
539 msgs.append("Dependency loop #%d found:\n" % len(valid_chains))
540 for dep in new_chain:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600541 msgs.append(" Task %s (dependent Tasks %s)\n" % (dep, self.runq_depends_names(self.runtaskentries[dep].depends)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500542 msgs.append("\n")
543 if len(valid_chains) > 10:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000544 msgs.append("Halted dependency loops search after 10 matches.\n")
Andrew Geissler99467da2019-02-25 18:54:23 -0600545 raise TooManyLoops
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500546 continue
547 scan = False
548 if revdep not in explored_deps:
549 scan = True
550 elif revdep in explored_deps[revdep]:
551 scan = True
552 else:
553 for dep in prev_chain:
554 if dep in explored_deps[revdep]:
555 scan = True
556 if scan:
557 find_chains(revdep, copy.deepcopy(prev_chain))
558 for dep in explored_deps[revdep]:
559 if dep not in total_deps:
560 total_deps.append(dep)
561
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600562 explored_deps[tid] = total_deps
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500563
Andrew Geissler99467da2019-02-25 18:54:23 -0600564 try:
565 for task in tasks:
566 find_chains(task, [])
567 except TooManyLoops:
568 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500569
570 return msgs
571
572 def calculate_task_weights(self, endpoints):
573 """
574 Calculate a number representing the "weight" of each task. Heavier weighted tasks
575 have more dependencies and hence should be executed sooner for maximum speed.
576
577 This function also sanity checks the task list finding tasks that are not
578 possible to execute due to circular dependencies.
579 """
580
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600581 numTasks = len(self.runtaskentries)
582 weight = {}
583 deps_left = {}
584 task_done = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500585
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600586 for tid in self.runtaskentries:
587 task_done[tid] = False
588 weight[tid] = 1
589 deps_left[tid] = len(self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500590
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600591 for tid in endpoints:
592 weight[tid] = 10
593 task_done[tid] = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500594
595 while True:
596 next_points = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600597 for tid in endpoints:
598 for revdep in self.runtaskentries[tid].depends:
599 weight[revdep] = weight[revdep] + weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500600 deps_left[revdep] = deps_left[revdep] - 1
601 if deps_left[revdep] == 0:
602 next_points.append(revdep)
603 task_done[revdep] = True
604 endpoints = next_points
Andrew Geissler595f6302022-01-24 19:11:47 +0000605 if not next_points:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500606 break
607
608 # Circular dependency sanity check
609 problem_tasks = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600610 for tid in self.runtaskentries:
611 if task_done[tid] is False or deps_left[tid] != 0:
612 problem_tasks.append(tid)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600613 logger.debug2("Task %s is not buildable", tid)
614 logger.debug2("(Complete marker was %s and the remaining dependency count was %s)\n", task_done[tid], deps_left[tid])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600615 self.runtaskentries[tid].weight = weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616
617 if problem_tasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600618 message = "%s unbuildable tasks were found.\n" % len(problem_tasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500619 message = message + "These are usually caused by circular dependencies and any circular dependency chains found will be printed below. Increase the debug level to see a list of unbuildable tasks.\n\n"
620 message = message + "Identifying dependency loops (this may take a short while)...\n"
621 logger.error(message)
622
623 msgs = self.circular_depchains_handler(problem_tasks)
624
625 message = "\n"
626 for msg in msgs:
627 message = message + msg
628 bb.msg.fatal("RunQueue", message)
629
630 return weight
631
632 def prepare(self):
633 """
634 Turn a set of taskData into a RunQueue and compute data needed
635 to optimise the execution order.
636 """
637
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600638 runq_build = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500639 recursivetasks = {}
640 recursiveitasks = {}
641 recursivetasksselfref = set()
642
643 taskData = self.taskData
644
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600645 found = False
646 for mc in self.taskData:
Andrew Geissler595f6302022-01-24 19:11:47 +0000647 if taskData[mc].taskentries:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600648 found = True
649 break
650 if not found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 # Nothing to do
652 return 0
653
Andrew Geissler517393d2023-01-13 08:55:19 -0600654 bb.parse.siggen.setup_datacache(self.dataCaches)
655
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600656 self.init_progress_reporter.start()
657 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600658 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500659
660 # Step A - Work out a list of tasks to run
661 #
662 # Taskdata gives us a list of possible providers for every build and run
663 # target ordered by priority. It also gives information on each of those
664 # providers.
665 #
666 # To create the actual list of tasks to execute we fix the list of
667 # providers and then resolve the dependencies into task IDs. This
668 # process is repeated for each type of dependency (tdepends, deptask,
669 # rdeptast, recrdeptask, idepends).
670
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600671 def add_build_dependencies(depids, tasknames, depends, mc):
672 for depname in depids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500673 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600674 if depname not in taskData[mc].build_targets or not taskData[mc].build_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500675 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600676 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500677 if depdata is None:
678 continue
679 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600680 t = depdata + ":" + taskname
681 if t in taskData[mc].taskentries:
682 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500683
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600684 def add_runtime_dependencies(depids, tasknames, depends, mc):
685 for depname in depids:
686 if depname not in taskData[mc].run_targets or not taskData[mc].run_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500687 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600688 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689 if depdata is None:
690 continue
691 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600692 t = depdata + ":" + taskname
693 if t in taskData[mc].taskentries:
694 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500695
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800696 def add_mc_dependencies(mc, tid):
697 mcdeps = taskData[mc].get_mcdepends()
698 for dep in mcdeps:
699 mcdependency = dep.split(':')
700 pn = mcdependency[3]
701 frommc = mcdependency[1]
702 mcdep = mcdependency[2]
703 deptask = mcdependency[4]
Andrew Geissler517393d2023-01-13 08:55:19 -0600704 if mcdep not in taskData:
705 bb.fatal("Multiconfig '%s' is referenced in multiconfig dependency '%s' but not enabled in BBMULTICONFIG?" % (mcdep, dep))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800706 if mc == frommc:
707 fn = taskData[mcdep].build_targets[pn][0]
708 newdep = '%s:%s' % (fn,deptask)
709 taskData[mc].taskentries[tid].tdepends.append(newdep)
710
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600711 for mc in taskData:
712 for tid in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500713
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600714 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
715 #runtid = build_tid(mc, fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500716
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600717 #logger.debug2("Processing %s,%s:%s", mc, fn, taskname)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600718
719 depends = set()
720 task_deps = self.dataCaches[mc].task_deps[taskfn]
721
722 self.runtaskentries[tid] = RunTaskEntry()
723
724 if fn in taskData[mc].failed_fns:
725 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500726
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800727 # We add multiconfig dependencies before processing internal task deps (tdepends)
728 if 'mcdepends' in task_deps and taskname in task_deps['mcdepends']:
729 add_mc_dependencies(mc, tid)
730
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731 # Resolve task internal dependencies
732 #
733 # e.g. addtask before X after Y
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600734 for t in taskData[mc].taskentries[tid].tdepends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800735 (depmc, depfn, deptaskname, _) = split_tid_mcfn(t)
736 depends.add(build_tid(depmc, depfn, deptaskname))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500737
738 # Resolve 'deptask' dependencies
739 #
740 # e.g. do_sometask[deptask] = "do_someothertask"
741 # (makes sure sometask runs after someothertask of all DEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600742 if 'deptask' in task_deps and taskname in task_deps['deptask']:
743 tasknames = task_deps['deptask'][taskname].split()
744 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500745
746 # Resolve 'rdeptask' dependencies
747 #
748 # e.g. do_sometask[rdeptask] = "do_someothertask"
749 # (makes sure sometask runs after someothertask of all RDEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600750 if 'rdeptask' in task_deps and taskname in task_deps['rdeptask']:
751 tasknames = task_deps['rdeptask'][taskname].split()
752 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500753
754 # Resolve inter-task dependencies
755 #
756 # e.g. do_sometask[depends] = "targetname:do_someothertask"
757 # (makes sure sometask runs after targetname's someothertask)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600758 idepends = taskData[mc].taskentries[tid].idepends
759 for (depname, idependtask) in idepends:
760 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 -0500761 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600762 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500763 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600764 t = depdata + ":" + idependtask
765 depends.add(t)
766 if t not in taskData[mc].taskentries:
767 bb.msg.fatal("RunQueue", "Task %s in %s depends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
768 irdepends = taskData[mc].taskentries[tid].irdepends
769 for (depname, idependtask) in irdepends:
770 if depname in taskData[mc].run_targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771 # Won't be in run_targets if ASSUME_PROVIDED
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500772 if not taskData[mc].run_targets[depname]:
773 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600774 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600776 t = depdata + ":" + idependtask
777 depends.add(t)
778 if t not in taskData[mc].taskentries:
779 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 -0500780
781 # Resolve recursive 'recrdeptask' dependencies (Part A)
782 #
783 # e.g. do_sometask[recrdeptask] = "do_someothertask"
784 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
785 # We cover the recursive part of the dependencies below
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600786 if 'recrdeptask' in task_deps and taskname in task_deps['recrdeptask']:
787 tasknames = task_deps['recrdeptask'][taskname].split()
788 recursivetasks[tid] = tasknames
789 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
790 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
791 if taskname in tasknames:
792 recursivetasksselfref.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500793
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600794 if 'recideptask' in task_deps and taskname in task_deps['recideptask']:
795 recursiveitasks[tid] = []
796 for t in task_deps['recideptask'][taskname].split():
797 newdep = build_tid(mc, fn, t)
798 recursiveitasks[tid].append(newdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500799
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600800 self.runtaskentries[tid].depends = depends
Brad Bishop316dfdd2018-06-25 12:45:53 -0400801 # Remove all self references
802 self.runtaskentries[tid].depends.discard(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500803
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600804 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500805
Brad Bishop316dfdd2018-06-25 12:45:53 -0400806 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600807 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400808
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500809 # Resolve recursive 'recrdeptask' dependencies (Part B)
810 #
811 # e.g. do_sometask[recrdeptask] = "do_someothertask"
812 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600813 # 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 -0600814
Brad Bishop316dfdd2018-06-25 12:45:53 -0400815 # Generating/interating recursive lists of dependencies is painful and potentially slow
816 # Precompute recursive task dependencies here by:
817 # a) create a temp list of reverse dependencies (revdeps)
818 # b) walk up the ends of the chains (when a given task no longer has dependencies i.e. len(deps) == 0)
819 # c) combine the total list of dependencies in cumulativedeps
820 # d) optimise by pre-truncating 'task' off the items in cumulativedeps (keeps items in sets lower)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500822
Brad Bishop316dfdd2018-06-25 12:45:53 -0400823 revdeps = {}
824 deps = {}
825 cumulativedeps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600826 for tid in self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400827 deps[tid] = set(self.runtaskentries[tid].depends)
828 revdeps[tid] = set()
829 cumulativedeps[tid] = set()
830 # Generate a temp list of reverse dependencies
831 for tid in self.runtaskentries:
832 for dep in self.runtaskentries[tid].depends:
833 revdeps[dep].add(tid)
834 # Find the dependency chain endpoints
835 endpoints = set()
836 for tid in self.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +0000837 if not deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400838 endpoints.add(tid)
839 # Iterate the chains collating dependencies
840 while endpoints:
841 next = set()
842 for tid in endpoints:
843 for dep in revdeps[tid]:
844 cumulativedeps[dep].add(fn_from_tid(tid))
845 cumulativedeps[dep].update(cumulativedeps[tid])
846 if tid in deps[dep]:
847 deps[dep].remove(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +0000848 if not deps[dep]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400849 next.add(dep)
850 endpoints = next
851 #for tid in deps:
Andrew Geissler595f6302022-01-24 19:11:47 +0000852 # if deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400853 # bb.warn("Sanity test failure, dependencies left for %s (%s)" % (tid, deps[tid]))
854
855 # Loop here since recrdeptasks can depend upon other recrdeptasks and we have to
856 # resolve these recursively until we aren't adding any further extra dependencies
857 extradeps = True
858 while extradeps:
859 extradeps = 0
860 for tid in recursivetasks:
861 tasknames = recursivetasks[tid]
862
863 totaldeps = set(self.runtaskentries[tid].depends)
864 if tid in recursiveitasks:
865 totaldeps.update(recursiveitasks[tid])
866 for dep in recursiveitasks[tid]:
867 if dep not in self.runtaskentries:
868 continue
869 totaldeps.update(self.runtaskentries[dep].depends)
870
871 deps = set()
872 for dep in totaldeps:
873 if dep in cumulativedeps:
874 deps.update(cumulativedeps[dep])
875
876 for t in deps:
877 for taskname in tasknames:
878 newtid = t + ":" + taskname
879 if newtid == tid:
880 continue
881 if newtid in self.runtaskentries and newtid not in self.runtaskentries[tid].depends:
882 extradeps += 1
883 self.runtaskentries[tid].depends.add(newtid)
884
885 # Handle recursive tasks which depend upon other recursive tasks
886 deps = set()
887 for dep in self.runtaskentries[tid].depends.intersection(recursivetasks):
888 deps.update(self.runtaskentries[dep].depends.difference(self.runtaskentries[tid].depends))
889 for newtid in deps:
890 for taskname in tasknames:
891 if not newtid.endswith(":" + taskname):
892 continue
893 if newtid in self.runtaskentries:
894 extradeps += 1
895 self.runtaskentries[tid].depends.add(newtid)
896
897 bb.debug(1, "Added %s recursive dependencies in this loop" % extradeps)
898
899 # Remove recrdeptask circular references so that do_a[recrdeptask] = "do_a do_b" can work
900 for tid in recursivetasksselfref:
901 self.runtaskentries[tid].depends.difference_update(recursivetasksselfref)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600902
903 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600904 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600905
906 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500907
908 # Step B - Mark all active tasks
909 #
910 # Start with the tasks we were asked to run and mark all dependencies
911 # as active too. If the task is to be 'forced', clear its stamp. Once
912 # all active tasks are marked, prune the ones we don't need.
913
914 logger.verbose("Marking Active Tasks")
915
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600916 def mark_active(tid, depth):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500917 """
918 Mark an item as active along with its depends
919 (calls itself recursively)
920 """
921
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600922 if tid in runq_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500923 return
924
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600925 runq_build[tid] = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500926
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600927 depends = self.runtaskentries[tid].depends
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500928 for depend in depends:
929 mark_active(depend, depth+1)
930
Brad Bishop79641f22019-09-10 07:20:22 -0400931 def invalidate_task(tid, error_nostamp):
932 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
933 taskdep = self.dataCaches[mc].task_deps[taskfn]
934 if fn + ":" + taskname not in taskData[mc].taskentries:
935 logger.warning("Task %s does not exist, invalidating this task will have no effect" % taskname)
936 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
937 if error_nostamp:
938 bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
939 else:
940 bb.debug(1, "Task %s is marked nostamp, cannot invalidate this task" % taskname)
941 else:
942 logger.verbose("Invalidate task %s, %s", taskname, fn)
Andrew Geissler517393d2023-01-13 08:55:19 -0600943 bb.parse.siggen.invalidate_task(taskname, taskfn)
Brad Bishop79641f22019-09-10 07:20:22 -0400944
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600945 self.target_tids = []
946 for (mc, target, task, fn) in self.targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500947
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600948 if target not in taskData[mc].build_targets or not taskData[mc].build_targets[target]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500949 continue
950
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600951 if target in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500952 continue
953
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500954 parents = False
955 if task.endswith('-'):
956 parents = True
957 task = task[:-1]
958
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600959 if fn in taskData[mc].failed_fns:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500960 continue
961
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600962 # fn already has mc prefix
963 tid = fn + ":" + task
964 self.target_tids.append(tid)
965 if tid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500966 import difflib
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600967 tasks = []
968 for x in taskData[mc].taskentries:
969 if x.startswith(fn + ":"):
970 tasks.append(taskname_from_tid(x))
971 close_matches = difflib.get_close_matches(task, tasks, cutoff=0.7)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500972 if close_matches:
973 extra = ". Close matches:\n %s" % "\n ".join(close_matches)
974 else:
975 extra = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600976 bb.msg.fatal("RunQueue", "Task %s does not exist for target %s (%s)%s" % (task, target, tid, extra))
977
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500978 # For tasks called "XXXX-", ony run their dependencies
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500979 if parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600980 for i in self.runtaskentries[tid].depends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500981 mark_active(i, 1)
982 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600983 mark_active(tid, 1)
984
985 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600986 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500987
988 # Step C - Prune all inactive tasks
989 #
990 # Once all active tasks are marked, prune the ones we don't need.
991
Brad Bishop316dfdd2018-06-25 12:45:53 -0400992 # Handle --runall
993 if self.cooker.configuration.runall:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500994 # re-run the mark_active and then drop unused tasks from new list
Andrew Geissler595f6302022-01-24 19:11:47 +0000995 reduced_tasklist = set(self.runtaskentries.keys())
996 for tid in list(self.runtaskentries.keys()):
997 if tid not in runq_build:
998 reduced_tasklist.remove(tid)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500999 runq_build = {}
Brad Bishop316dfdd2018-06-25 12:45:53 -04001000
1001 for task in self.cooker.configuration.runall:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001002 if not task.startswith("do_"):
1003 task = "do_{0}".format(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001004 runall_tids = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00001005 for tid in reduced_tasklist:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001006 wanttid = "{0}:{1}".format(fn_from_tid(tid), task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001007 if wanttid in self.runtaskentries:
1008 runall_tids.add(wanttid)
1009
1010 for tid in list(runall_tids):
Andrew Geissler595f6302022-01-24 19:11:47 +00001011 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001012 if self.cooker.configuration.force:
1013 invalidate_task(tid, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001014
Andrew Geissler595f6302022-01-24 19:11:47 +00001015 delcount = set()
1016 for tid in list(self.runtaskentries.keys()):
1017 if tid not in runq_build:
1018 delcount.add(tid)
1019 del self.runtaskentries[tid]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001020
Andrew Geissler595f6302022-01-24 19:11:47 +00001021 if self.cooker.configuration.runall:
1022 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001023 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)))
1024
1025 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001026 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001027
1028 # Handle runonly
1029 if self.cooker.configuration.runonly:
1030 # re-run the mark_active and then drop unused tasks from new list
1031 runq_build = {}
1032
1033 for task in self.cooker.configuration.runonly:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001034 if not task.startswith("do_"):
1035 task = "do_{0}".format(task)
Andrew Geissler595f6302022-01-24 19:11:47 +00001036 runonly_tids = [k for k in self.runtaskentries.keys() if taskname_from_tid(k) == task]
Brad Bishop316dfdd2018-06-25 12:45:53 -04001037
Andrew Geissler595f6302022-01-24 19:11:47 +00001038 for tid in runonly_tids:
1039 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001040 if self.cooker.configuration.force:
1041 invalidate_task(tid, False)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001042
1043 for tid in list(self.runtaskentries.keys()):
1044 if tid not in runq_build:
Andrew Geissler595f6302022-01-24 19:11:47 +00001045 delcount.add(tid)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001046 del self.runtaskentries[tid]
1047
Andrew Geissler595f6302022-01-24 19:11:47 +00001048 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001049 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 -05001050
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001051 #
1052 # Step D - Sanity checks and computation
1053 #
1054
1055 # Check to make sure we still have tasks to run
Andrew Geissler595f6302022-01-24 19:11:47 +00001056 if not self.runtaskentries:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001057 if not taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001058 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.")
1059 else:
1060 bb.msg.fatal("RunQueue", "No active tasks and not in --continue mode?! Please report this bug.")
1061
Brad Bishop316dfdd2018-06-25 12:45:53 -04001062 logger.verbose("Pruned %s inactive tasks, %s left", len(delcount), len(self.runtaskentries))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001063
1064 logger.verbose("Assign Weightings")
1065
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001066 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001067 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001068
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001069 # Generate a list of reverse dependencies to ease future calculations
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001070 for tid in self.runtaskentries:
1071 for dep in self.runtaskentries[tid].depends:
1072 self.runtaskentries[dep].revdeps.add(tid)
1073
1074 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001075 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001076
1077 # Identify tasks at the end of dependency chains
1078 # Error on circular dependency loops (length two)
1079 endpoints = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001080 for tid in self.runtaskentries:
1081 revdeps = self.runtaskentries[tid].revdeps
Andrew Geissler595f6302022-01-24 19:11:47 +00001082 if not revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001083 endpoints.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001084 for dep in revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001085 if dep in self.runtaskentries[tid].depends:
1086 bb.msg.fatal("RunQueue", "Task %s has circular dependency on %s" % (tid, dep))
1087
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001088
1089 logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
1090
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001091 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001092 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001093
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001094 # Calculate task weights
1095 # Check of higher length circular dependencies
1096 self.runq_weight = self.calculate_task_weights(endpoints)
1097
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001098 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001099 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001100
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001101 # Sanity Check - Check for multiple tasks building the same provider
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001102 for mc in self.dataCaches:
1103 prov_list = {}
1104 seen_fn = []
1105 for tid in self.runtaskentries:
1106 (tidmc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1107 if taskfn in seen_fn:
1108 continue
1109 if mc != tidmc:
1110 continue
1111 seen_fn.append(taskfn)
1112 for prov in self.dataCaches[mc].fn_provides[taskfn]:
1113 if prov not in prov_list:
1114 prov_list[prov] = [taskfn]
1115 elif taskfn not in prov_list[prov]:
1116 prov_list[prov].append(taskfn)
1117 for prov in prov_list:
1118 if len(prov_list[prov]) < 2:
1119 continue
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001120 if prov in self.multi_provider_allowed:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001121 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001122 seen_pn = []
1123 # If two versions of the same PN are being built its fatal, we don't support it.
1124 for fn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001125 pn = self.dataCaches[mc].pkg_fn[fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001126 if pn not in seen_pn:
1127 seen_pn.append(pn)
1128 else:
1129 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 +00001130 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 -05001131 #
1132 # Construct a list of things which uniquely depend on each provider
1133 # since this may help the user figure out which dependency is triggering this warning
1134 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001135 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 -05001136 deplist = {}
1137 commondeps = None
1138 for provfn in prov_list[prov]:
1139 deps = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001140 for tid in self.runtaskentries:
1141 fn = fn_from_tid(tid)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001142 if fn != provfn:
1143 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001144 for dep in self.runtaskentries[tid].revdeps:
1145 fn = fn_from_tid(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001146 if fn == provfn:
1147 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001148 deps.add(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001149 if not commondeps:
1150 commondeps = set(deps)
1151 else:
1152 commondeps &= deps
1153 deplist[provfn] = deps
1154 for provfn in deplist:
Andrew Geissler595f6302022-01-24 19:11:47 +00001155 msgs.append("\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001156 #
1157 # Construct a list of provides and runtime providers for each recipe
1158 # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC)
1159 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001160 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 -05001161 provide_results = {}
1162 rprovide_results = {}
1163 commonprovs = None
1164 commonrprovs = None
1165 for provfn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001166 provides = set(self.dataCaches[mc].fn_provides[provfn])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001167 rprovides = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001168 for rprovide in self.dataCaches[mc].rproviders:
1169 if provfn in self.dataCaches[mc].rproviders[rprovide]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001170 rprovides.add(rprovide)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001171 for package in self.dataCaches[mc].packages:
1172 if provfn in self.dataCaches[mc].packages[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001173 rprovides.add(package)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001174 for package in self.dataCaches[mc].packages_dynamic:
1175 if provfn in self.dataCaches[mc].packages_dynamic[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001176 rprovides.add(package)
1177 if not commonprovs:
1178 commonprovs = set(provides)
1179 else:
1180 commonprovs &= provides
1181 provide_results[provfn] = provides
1182 if not commonrprovs:
1183 commonrprovs = set(rprovides)
1184 else:
1185 commonrprovs &= rprovides
1186 rprovide_results[provfn] = rprovides
Andrew Geissler595f6302022-01-24 19:11:47 +00001187 #msgs.append("\nCommon provides:\n %s" % ("\n ".join(commonprovs)))
1188 #msgs.append("\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001189 for provfn in prov_list[prov]:
Andrew Geissler595f6302022-01-24 19:11:47 +00001190 msgs.append("\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs)))
1191 msgs.append("\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001192
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001193 if self.warn_multi_bb:
Andrew Geissler595f6302022-01-24 19:11:47 +00001194 logger.verbnote("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001195 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00001196 logger.error("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001197
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001198 self.init_progress_reporter.next_stage()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001199 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001200 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001201
1202 # Iterate over the task list looking for tasks with a 'setscene' function
Andrew Geissler82c905d2020-04-13 13:39:40 -05001203 self.runq_setscene_tids = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001204 if not self.cooker.configuration.nosetscene:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001205 for tid in self.runtaskentries:
1206 (mc, fn, taskname, _) = split_tid_mcfn(tid)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001207 setscenetid = tid + "_setscene"
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001208 if setscenetid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001209 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -05001210 self.runq_setscene_tids.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001211
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001212 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001213 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001214
1215 # Invalidate task if force mode active
1216 if self.cooker.configuration.force:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001217 for tid in self.target_tids:
1218 invalidate_task(tid, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001219
1220 # Invalidate task if invalidate mode active
1221 if self.cooker.configuration.invalidate_stamp:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001222 for tid in self.target_tids:
1223 fn = fn_from_tid(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001224 for st in self.cooker.configuration.invalidate_stamp.split(','):
1225 if not st.startswith("do_"):
1226 st = "do_%s" % st
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001227 invalidate_task(fn + ":" + st, True)
1228
1229 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001230 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001231
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001232 # Create and print to the logs a virtual/xxxx -> PN (fn) table
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001233 for mc in taskData:
1234 virtmap = taskData[mc].get_providermap(prefix="virtual/")
1235 virtpnmap = {}
1236 for v in virtmap:
1237 virtpnmap[v] = self.dataCaches[mc].pkg_fn[virtmap[v]]
1238 bb.debug(2, "%s resolved to: %s (%s)" % (v, virtpnmap[v], virtmap[v]))
1239 if hasattr(bb.parse.siggen, "tasks_resolved"):
1240 bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
1241
1242 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001243 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001244
Brad Bishop00e122a2019-10-05 11:10:57 -04001245 bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
1246
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001247 # Iterate over the task list and call into the siggen code
1248 dealtwith = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001249 todeal = set(self.runtaskentries)
Andrew Geissler595f6302022-01-24 19:11:47 +00001250 while todeal:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001251 for tid in todeal.copy():
Andrew Geissler595f6302022-01-24 19:11:47 +00001252 if not (self.runtaskentries[tid].depends - dealtwith):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001253 dealtwith.add(tid)
1254 todeal.remove(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001255 self.prepare_task_hash(tid)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001256 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001257
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001258 bb.parse.siggen.writeout_file_checksum_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001259
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001260 #self.dump_data()
1261 return len(self.runtaskentries)
1262
Brad Bishop19323692019-04-05 15:28:33 -04001263 def prepare_task_hash(self, tid):
Andrew Geissler517393d2023-01-13 08:55:19 -06001264 bb.parse.siggen.prep_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
1265 self.runtaskentries[tid].hash = bb.parse.siggen.get_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
Brad Bishop08902b02019-08-20 09:16:51 -04001266 self.runtaskentries[tid].unihash = bb.parse.siggen.get_unihash(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001267
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001268 def dump_data(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001269 """
1270 Dump some debug information on the internal data structures
1271 """
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001272 logger.debug3("run_tasks:")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001273 for tid in self.runtaskentries:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001274 logger.debug3(" %s: %s Deps %s RevDeps %s", tid,
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001275 self.runtaskentries[tid].weight,
1276 self.runtaskentries[tid].depends,
1277 self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001278
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001279class RunQueueWorker():
1280 def __init__(self, process, pipe):
1281 self.process = process
1282 self.pipe = pipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001283
1284class RunQueue:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001285 def __init__(self, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001286
1287 self.cooker = cooker
1288 self.cfgData = cfgData
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001289 self.rqdata = RunQueueData(self, cooker, cfgData, dataCaches, taskData, targets)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001290
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001291 self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION") or None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001292 self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID") or None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001293
1294 self.state = runQueuePrepare
1295
1296 # For disk space monitor
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001297 # Invoked at regular time intervals via the bitbake heartbeat event
1298 # while the build is running. We generate a unique name for the handler
1299 # here, just in case that there ever is more than one RunQueue instance,
Brad Bishop96ff1982019-08-19 13:50:42 -04001300 # start the handler when reaching runQueueSceneInit, and stop it when
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001301 # done with the build.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001302 self.dm = monitordisk.diskMonitor(cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001303 self.dm_event_handler_name = '_bb_diskmonitor_' + str(id(self))
1304 self.dm_event_handler_registered = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001305 self.rqexe = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001306 self.worker = {}
1307 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001308
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001309 def _start_worker(self, mc, fakeroot = False, rqexec = None):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001310 logger.debug("Starting bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001311 magic = "decafbad"
1312 if self.cooker.configuration.profile:
1313 magic = "decafbadbad"
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001314 fakerootlogs = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001315 if fakeroot:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001316 magic = magic + "beef"
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001317 mcdata = self.cooker.databuilder.mcdata[mc]
Brad Bishop19323692019-04-05 15:28:33 -04001318 fakerootcmd = shlex.split(mcdata.getVar("FAKEROOTCMD"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001319 fakerootenv = (mcdata.getVar("FAKEROOTBASEENV") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001320 env = os.environ.copy()
1321 for key, value in (var.split('=') for var in fakerootenv):
1322 env[key] = value
Brad Bishop19323692019-04-05 15:28:33 -04001323 worker = subprocess.Popen(fakerootcmd + ["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001324 fakerootlogs = self.rqdata.dataCaches[mc].fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001325 else:
1326 worker = subprocess.Popen(["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
1327 bb.utils.nonblockingfd(worker.stdout)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001328 workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, self, rqexec, fakerootlogs=fakerootlogs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001329
1330 workerdata = {
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001331 "sigdata" : bb.parse.siggen.get_taskdata(),
Andrew Geissler82c905d2020-04-13 13:39:40 -05001332 "logdefaultlevel" : bb.msg.loggerDefaultLogLevel,
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001333 "build_verbose_shell" : self.cooker.configuration.build_verbose_shell,
1334 "build_verbose_stdout" : self.cooker.configuration.build_verbose_stdout,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001335 "logdefaultdomain" : bb.msg.loggerDefaultDomains,
1336 "prhost" : self.cooker.prhost,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001337 "buildname" : self.cfgData.getVar("BUILDNAME"),
1338 "date" : self.cfgData.getVar("DATE"),
1339 "time" : self.cfgData.getVar("TIME"),
Brad Bishopa34c0302019-09-23 22:34:48 -04001340 "hashservaddr" : self.cooker.hashservaddr,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001341 "umask" : self.cfgData.getVar("BB_DEFAULT_UMASK"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001342 }
1343
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001344 worker.stdin.write(b"<cookerconfig>" + pickle.dumps(self.cooker.configuration) + b"</cookerconfig>")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001345 worker.stdin.write(b"<extraconfigdata>" + pickle.dumps(self.cooker.extraconfigdata) + b"</extraconfigdata>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001346 worker.stdin.write(b"<workerdata>" + pickle.dumps(workerdata) + b"</workerdata>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001347 worker.stdin.flush()
1348
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001349 return RunQueueWorker(worker, workerpipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001350
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001351 def _teardown_worker(self, worker):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001352 if not worker:
1353 return
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001354 logger.debug("Teardown for bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001355 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001356 worker.process.stdin.write(b"<quit></quit>")
1357 worker.process.stdin.flush()
1358 worker.process.stdin.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001359 except IOError:
1360 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001361 while worker.process.returncode is None:
1362 worker.pipe.read()
1363 worker.process.poll()
1364 while worker.pipe.read():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001365 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001366 worker.pipe.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001367
1368 def start_worker(self):
1369 if self.worker:
1370 self.teardown_workers()
1371 self.teardown = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001372 for mc in self.rqdata.dataCaches:
1373 self.worker[mc] = self._start_worker(mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001374
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001375 def start_fakeworker(self, rqexec, mc):
1376 if not mc in self.fakeworker:
1377 self.fakeworker[mc] = self._start_worker(mc, True, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001378
1379 def teardown_workers(self):
1380 self.teardown = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001381 for mc in self.worker:
1382 self._teardown_worker(self.worker[mc])
1383 self.worker = {}
1384 for mc in self.fakeworker:
1385 self._teardown_worker(self.fakeworker[mc])
1386 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001387
1388 def read_workers(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001389 for mc in self.worker:
1390 self.worker[mc].pipe.read()
1391 for mc in self.fakeworker:
1392 self.fakeworker[mc].pipe.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001393
1394 def active_fds(self):
1395 fds = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001396 for mc in self.worker:
1397 fds.append(self.worker[mc].pipe.input)
1398 for mc in self.fakeworker:
1399 fds.append(self.fakeworker[mc].pipe.input)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001400 return fds
1401
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001402 def check_stamp_task(self, tid, taskname = None, recurse = False, cache = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001403 def get_timestamp(f):
1404 try:
1405 if not os.access(f, os.F_OK):
1406 return None
1407 return os.stat(f)[stat.ST_MTIME]
1408 except:
1409 return None
1410
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001411 (mc, fn, tn, taskfn) = split_tid_mcfn(tid)
1412 if taskname is None:
1413 taskname = tn
1414
Andrew Geissler517393d2023-01-13 08:55:19 -06001415 stampfile = bb.parse.siggen.stampfile_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001416
1417 # If the stamp is missing, it's not current
1418 if not os.access(stampfile, os.F_OK):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001419 logger.debug2("Stampfile %s not available", stampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001420 return False
1421 # If it's a 'nostamp' task, it's not current
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001422 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001423 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001424 logger.debug2("%s.%s is nostamp\n", fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001425 return False
1426
Andrew Geissler517393d2023-01-13 08:55:19 -06001427 if taskname.endswith("_setscene"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001428 return True
1429
1430 if cache is None:
1431 cache = {}
1432
1433 iscurrent = True
1434 t1 = get_timestamp(stampfile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001435 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001436 if iscurrent:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001437 (mc2, fn2, taskname2, taskfn2) = split_tid_mcfn(dep)
Andrew Geissler517393d2023-01-13 08:55:19 -06001438 stampfile2 = bb.parse.siggen.stampfile_mcfn(taskname2, taskfn2)
1439 stampfile3 = bb.parse.siggen.stampfile_mcfn(taskname2 + "_setscene", taskfn2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001440 t2 = get_timestamp(stampfile2)
1441 t3 = get_timestamp(stampfile3)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001442 if t3 and not t2:
1443 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001444 if t3 and t3 > t2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001445 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00001446 if fn == fn2:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001447 if not t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001448 logger.debug2('Stampfile %s does not exist', stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001449 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001450 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001451 if t1 < t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001452 logger.debug2('Stampfile %s < %s', stampfile, stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001453 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001454 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001455 if recurse and iscurrent:
1456 if dep in cache:
1457 iscurrent = cache[dep]
1458 if not iscurrent:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001459 logger.debug2('Stampfile for dependency %s:%s invalid (cached)' % (fn2, taskname2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001460 else:
1461 iscurrent = self.check_stamp_task(dep, recurse=True, cache=cache)
1462 cache[dep] = iscurrent
1463 if recurse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001464 cache[tid] = iscurrent
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001465 return iscurrent
1466
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001467 def validate_hashes(self, tocheck, data, currentcount=0, siginfo=False, summary=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04001468 valid = set()
1469 if self.hashvalidate:
Brad Bishop08902b02019-08-20 09:16:51 -04001470 sq_data = {}
1471 sq_data['hash'] = {}
1472 sq_data['hashfn'] = {}
1473 sq_data['unihash'] = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04001474 for tid in tocheck:
1475 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04001476 sq_data['hash'][tid] = self.rqdata.runtaskentries[tid].hash
1477 sq_data['hashfn'][tid] = self.rqdata.dataCaches[mc].hashfn[taskfn]
1478 sq_data['unihash'][tid] = self.rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -04001479
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001480 valid = self.validate_hash(sq_data, data, siginfo, currentcount, summary)
Brad Bishop96ff1982019-08-19 13:50:42 -04001481
1482 return valid
1483
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001484 def validate_hash(self, sq_data, d, siginfo, currentcount, summary):
1485 locs = {"sq_data" : sq_data, "d" : d, "siginfo" : siginfo, "currentcount" : currentcount, "summary" : summary}
Brad Bishop19323692019-04-05 15:28:33 -04001486
Brad Bishop08902b02019-08-20 09:16:51 -04001487 # Metadata has **kwargs so args can be added, sq_data can also gain new fields
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001488 call = self.hashvalidate + "(sq_data, d, siginfo=siginfo, currentcount=currentcount, summary=summary)"
Brad Bishop19323692019-04-05 15:28:33 -04001489
Brad Bishop19323692019-04-05 15:28:33 -04001490 return bb.utils.better_eval(call, locs)
1491
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001492 def _execute_runqueue(self):
1493 """
1494 Run the tasks in a queue prepared by rqdata.prepare()
1495 Upon failure, optionally try to recover the build using any alternate providers
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001496 (if the halt on failure configuration option isn't set)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001497 """
1498
1499 retval = True
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001500 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001501
1502 if self.state is runQueuePrepare:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001503 # NOTE: if you add, remove or significantly refactor the stages of this
1504 # process then you should recalculate the weightings here. This is quite
1505 # easy to do - just change the next line temporarily to pass debug=True as
1506 # the last parameter and you'll get a printout of the weightings as well
1507 # as a map to the lines where next_stage() was called. Of course this isn't
1508 # critical, but it helps to keep the progress reporting accurate.
1509 self.rqdata.init_progress_reporter = bb.progress.MultiStageProcessProgressReporter(self.cooker.data,
1510 "Initialising tasks",
1511 [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 -05001512 if self.rqdata.prepare() == 0:
1513 self.state = runQueueComplete
1514 else:
1515 self.state = runQueueSceneInit
Brad Bishop00e122a2019-10-05 11:10:57 -04001516 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001517
1518 if self.state is runQueueSceneInit:
Brad Bishop96ff1982019-08-19 13:50:42 -04001519 self.rqdata.init_progress_reporter.next_stage()
1520
1521 # we are ready to run, emit dependency info to any UI or class which
1522 # needs it
1523 depgraph = self.cooker.buildDependTree(self, self.rqdata.taskData)
1524 self.rqdata.init_progress_reporter.next_stage()
1525 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
1526
Brad Bishope2d5b612018-11-23 10:55:50 +13001527 if not self.dm_event_handler_registered:
1528 res = bb.event.register(self.dm_event_handler_name,
Andrew Geissler517393d2023-01-13 08:55:19 -06001529 lambda x, y: self.dm.check(self) if self.state in [runQueueRunning, runQueueCleanUp] else False,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001530 ('bb.event.HeartbeatEvent',), data=self.cfgData)
Brad Bishope2d5b612018-11-23 10:55:50 +13001531 self.dm_event_handler_registered = True
1532
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001533 dump = self.cooker.configuration.dump_signatures
1534 if dump:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001535 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001536 if 'printdiff' in dump:
1537 invalidtasks = self.print_diffscenetasks()
1538 self.dump_signatures(dump)
1539 if 'printdiff' in dump:
1540 self.write_diffscenetasks(invalidtasks)
1541 self.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001542
Brad Bishop96ff1982019-08-19 13:50:42 -04001543 if self.state is runQueueSceneInit:
1544 self.rqdata.init_progress_reporter.next_stage()
1545 self.start_worker()
1546 self.rqdata.init_progress_reporter.next_stage()
1547 self.rqexe = RunQueueExecute(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001548
Brad Bishop96ff1982019-08-19 13:50:42 -04001549 # If we don't have any setscene functions, skip execution
Andrew Geissler595f6302022-01-24 19:11:47 +00001550 if not self.rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04001551 logger.info('No setscene tasks')
1552 for tid in self.rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00001553 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04001554 self.rqexe.setbuildable(tid)
1555 self.rqexe.tasks_notcovered.add(tid)
1556 self.rqexe.sqdone = True
1557 logger.info('Executing Tasks')
1558 self.state = runQueueRunning
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001559
1560 if self.state is runQueueRunning:
1561 retval = self.rqexe.execute()
1562
1563 if self.state is runQueueCleanUp:
1564 retval = self.rqexe.finish()
1565
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001566 build_done = self.state is runQueueComplete or self.state is runQueueFailed
1567
1568 if build_done and self.dm_event_handler_registered:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001569 bb.event.remove(self.dm_event_handler_name, None, data=self.cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001570 self.dm_event_handler_registered = False
1571
1572 if build_done and self.rqexe:
Brad Bishop08902b02019-08-20 09:16:51 -04001573 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001574 self.teardown_workers()
Brad Bishop96ff1982019-08-19 13:50:42 -04001575 if self.rqexe:
1576 if self.rqexe.stats.failed:
1577 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)
1578 else:
1579 # Let's avoid the word "failed" if nothing actually did
1580 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 -05001581
1582 if self.state is runQueueFailed:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001583 raise bb.runqueue.TaskFailure(self.rqexe.failed_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001584
1585 if self.state is runQueueComplete:
1586 # All done
1587 return False
1588
1589 # Loop
1590 return retval
1591
1592 def execute_runqueue(self):
1593 # Catch unexpected exceptions and ensure we exit when an error occurs, not loop.
1594 try:
1595 return self._execute_runqueue()
1596 except bb.runqueue.TaskFailure:
1597 raise
1598 except SystemExit:
1599 raise
1600 except bb.BBHandledException:
1601 try:
1602 self.teardown_workers()
1603 except:
1604 pass
1605 self.state = runQueueComplete
1606 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001607 except Exception as err:
1608 logger.exception("An uncaught exception occurred in runqueue")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001609 try:
1610 self.teardown_workers()
1611 except:
1612 pass
1613 self.state = runQueueComplete
1614 raise
1615
1616 def finish_runqueue(self, now = False):
1617 if not self.rqexe:
1618 self.state = runQueueComplete
1619 return
1620
1621 if now:
1622 self.rqexe.finish_now()
1623 else:
1624 self.rqexe.finish()
1625
Andrew Geissler517393d2023-01-13 08:55:19 -06001626 def _rq_dump_sigtid(self, tids):
1627 for tid in tids:
1628 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1629 dataCaches = self.rqdata.dataCaches
1630 bb.parse.siggen.dump_sigtask(taskfn, taskname, dataCaches[mc].stamp[taskfn], True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001631
1632 def dump_signatures(self, options):
Andrew Geissler517393d2023-01-13 08:55:19 -06001633 if bb.cooker.CookerFeatures.RECIPE_SIGGEN_INFO not in self.cooker.featureset:
1634 bb.fatal("The dump signatures functionality needs the RECIPE_SIGGEN_INFO feature enabled")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001635
Andrew Geissler517393d2023-01-13 08:55:19 -06001636 bb.note("Writing task signature files")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001637
1638 max_process = int(self.cfgData.getVar("BB_NUMBER_PARSE_THREADS") or os.cpu_count() or 1)
Andrew Geissler517393d2023-01-13 08:55:19 -06001639 def chunkify(l, n):
1640 return [l[i::n] for i in range(n)]
1641 tids = chunkify(list(self.rqdata.runtaskentries), max_process)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001642 # We cannot use the real multiprocessing.Pool easily due to some local data
1643 # that can't be pickled. This is a cheap multi-process solution.
1644 launched = []
Andrew Geissler517393d2023-01-13 08:55:19 -06001645 while tids:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001646 if len(launched) < max_process:
Andrew Geissler517393d2023-01-13 08:55:19 -06001647 p = Process(target=self._rq_dump_sigtid, args=(tids.pop(), ))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001648 p.start()
1649 launched.append(p)
1650 for q in launched:
1651 # The finished processes are joined when calling is_alive()
1652 if not q.is_alive():
1653 launched.remove(q)
1654 for p in launched:
1655 p.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001656
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001657 bb.parse.siggen.dump_sigs(self.rqdata.dataCaches, options)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001658
1659 return
1660
1661 def print_diffscenetasks(self):
1662
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001663 noexec = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001664 tocheck = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001665
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001666 for tid in self.rqdata.runtaskentries:
1667 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1668 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001669
1670 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001671 noexec.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001672 continue
1673
Brad Bishop96ff1982019-08-19 13:50:42 -04001674 tocheck.add(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001675
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001676 valid_new = self.validate_hashes(tocheck, self.cooker.data, 0, True, summary=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001677
1678 # Tasks which are both setscene and noexec never care about dependencies
1679 # We therefore find tasks which are setscene and noexec and mark their
1680 # unique dependencies as valid.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001681 for tid in noexec:
1682 if tid not in self.rqdata.runq_setscene_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001683 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001684 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001685 hasnoexecparents = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001686 for dep2 in self.rqdata.runtaskentries[dep].revdeps:
1687 if dep2 in self.rqdata.runq_setscene_tids and dep2 in noexec:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001688 continue
1689 hasnoexecparents = False
1690 break
1691 if hasnoexecparents:
1692 valid_new.add(dep)
1693
1694 invalidtasks = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001695 for tid in self.rqdata.runtaskentries:
1696 if tid not in valid_new and tid not in noexec:
1697 invalidtasks.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001698
1699 found = set()
1700 processed = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001701 for tid in invalidtasks:
1702 toprocess = set([tid])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001703 while toprocess:
1704 next = set()
1705 for t in toprocess:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001706 for dep in self.rqdata.runtaskentries[t].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001707 if dep in invalidtasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001708 found.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001709 if dep not in processed:
1710 processed.add(dep)
1711 next.add(dep)
1712 toprocess = next
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001713 if tid in found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001714 toprocess = set()
1715
1716 tasklist = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001717 for tid in invalidtasks.difference(found):
1718 tasklist.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001719
1720 if tasklist:
1721 bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
1722
1723 return invalidtasks.difference(found)
1724
1725 def write_diffscenetasks(self, invalidtasks):
1726
1727 # Define recursion callback
1728 def recursecb(key, hash1, hash2):
1729 hashes = [hash1, hash2]
1730 hashfiles = bb.siggen.find_siginfo(key, None, hashes, self.cfgData)
1731
1732 recout = []
1733 if len(hashfiles) == 2:
1734 out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
Brad Bishopc342db32019-05-15 21:57:59 -04001735 recout.extend(list(' ' + l for l in out2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001736 else:
1737 recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
1738
1739 return recout
1740
1741
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001742 for tid in invalidtasks:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001743 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1744 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001745 h = self.rqdata.runtaskentries[tid].hash
Patrick Williams03907ee2022-05-01 06:28:52 -05001746 matches = bb.siggen.find_siginfo(pn, taskname, [], self.cooker.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001747 match = None
1748 for m in matches:
1749 if h in m:
1750 match = m
1751 if match is None:
1752 bb.fatal("Can't find a task we're supposed to have written out? (hash: %s)?" % h)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001753 matches = {k : v for k, v in iter(matches.items()) if h not in k}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001754 if matches:
1755 latestmatch = sorted(matches.keys(), key=lambda f: matches[f])[-1]
Brad Bishop19323692019-04-05 15:28:33 -04001756 prevh = __find_sha256__.search(latestmatch).group(0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001757 output = bb.siggen.compare_sigfiles(latestmatch, match, recursecb)
1758 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))
1759
Brad Bishop96ff1982019-08-19 13:50:42 -04001760
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001761class RunQueueExecute:
1762
1763 def __init__(self, rq):
1764 self.rq = rq
1765 self.cooker = rq.cooker
1766 self.cfgData = rq.cfgData
1767 self.rqdata = rq.rqdata
1768
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001769 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
1770 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001771 self.max_cpu_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_CPU")
1772 self.max_io_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_IO")
Patrick Williams92b42cb2022-09-03 06:53:57 -05001773 self.max_memory_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_MEMORY")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001774
Brad Bishop96ff1982019-08-19 13:50:42 -04001775 self.sq_buildable = set()
1776 self.sq_running = set()
1777 self.sq_live = set()
1778
Brad Bishop08902b02019-08-20 09:16:51 -04001779 self.updated_taskhash_queue = []
1780 self.pending_migrations = set()
1781
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001782 self.runq_buildable = set()
1783 self.runq_running = set()
1784 self.runq_complete = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001785 self.runq_tasksrun = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001786
1787 self.build_stamps = {}
1788 self.build_stamps2 = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001789 self.failed_tids = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001790 self.sq_deferred = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001791
1792 self.stampcache = {}
1793
Brad Bishop08902b02019-08-20 09:16:51 -04001794 self.holdoff_tasks = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04001795 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04001796 self.sqdone = False
1797
Andrew Geissler5199d832021-09-24 16:47:35 -05001798 self.stats = RunQueueStats(len(self.rqdata.runtaskentries), len(self.rqdata.runq_setscene_tids))
Brad Bishop96ff1982019-08-19 13:50:42 -04001799
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001800 for mc in rq.worker:
1801 rq.worker[mc].pipe.setrunqueueexec(self)
1802 for mc in rq.fakeworker:
1803 rq.fakeworker[mc].pipe.setrunqueueexec(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001804
1805 if self.number_tasks <= 0:
1806 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks)
1807
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001808 lower_limit = 1.0
1809 upper_limit = 1000000.0
1810 if self.max_cpu_pressure:
1811 self.max_cpu_pressure = float(self.max_cpu_pressure)
1812 if self.max_cpu_pressure < lower_limit:
1813 bb.fatal("Invalid BB_PRESSURE_MAX_CPU %s, minimum value is %s." % (self.max_cpu_pressure, lower_limit))
1814 if self.max_cpu_pressure > upper_limit:
1815 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))
1816
1817 if self.max_io_pressure:
1818 self.max_io_pressure = float(self.max_io_pressure)
1819 if self.max_io_pressure < lower_limit:
1820 bb.fatal("Invalid BB_PRESSURE_MAX_IO %s, minimum value is %s." % (self.max_io_pressure, lower_limit))
1821 if self.max_io_pressure > upper_limit:
1822 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))
1823
Patrick Williams92b42cb2022-09-03 06:53:57 -05001824 if self.max_memory_pressure:
1825 self.max_memory_pressure = float(self.max_memory_pressure)
1826 if self.max_memory_pressure < lower_limit:
1827 bb.fatal("Invalid BB_PRESSURE_MAX_MEMORY %s, minimum value is %s." % (self.max_memory_pressure, lower_limit))
1828 if self.max_memory_pressure > upper_limit:
1829 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))
1830
Brad Bishop96ff1982019-08-19 13:50:42 -04001831 # List of setscene tasks which we've covered
1832 self.scenequeue_covered = set()
1833 # List of tasks which are covered (including setscene ones)
1834 self.tasks_covered = set()
1835 self.tasks_scenequeue_done = set()
1836 self.scenequeue_notcovered = set()
1837 self.tasks_notcovered = set()
1838 self.scenequeue_notneeded = set()
1839
Brad Bishop08902b02019-08-20 09:16:51 -04001840 # We can't skip specified target tasks which aren't setscene tasks
1841 self.cantskip = set(self.rqdata.target_tids)
1842 self.cantskip.difference_update(self.rqdata.runq_setscene_tids)
1843 self.cantskip.intersection_update(self.rqdata.runtaskentries)
Brad Bishop96ff1982019-08-19 13:50:42 -04001844
1845 schedulers = self.get_schedulers()
1846 for scheduler in schedulers:
1847 if self.scheduler == scheduler.name:
1848 self.sched = scheduler(self, self.rqdata)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001849 logger.debug("Using runqueue scheduler '%s'", scheduler.name)
Brad Bishop96ff1982019-08-19 13:50:42 -04001850 break
1851 else:
1852 bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
1853 (self.scheduler, ", ".join(obj.name for obj in schedulers)))
1854
Andrew Geissler595f6302022-01-24 19:11:47 +00001855 #if self.rqdata.runq_setscene_tids:
Brad Bishop08902b02019-08-20 09:16:51 -04001856 self.sqdata = SQData()
1857 build_scenequeue_data(self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self)
Brad Bishop96ff1982019-08-19 13:50:42 -04001858
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001859 def runqueue_process_waitpid(self, task, status, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001860
1861 # self.build_stamps[pid] may not exist when use shared work directory.
1862 if task in self.build_stamps:
1863 self.build_stamps2.remove(self.build_stamps[task])
1864 del self.build_stamps[task]
1865
Brad Bishop96ff1982019-08-19 13:50:42 -04001866 if task in self.sq_live:
1867 if status != 0:
1868 self.sq_task_fail(task, status)
1869 else:
1870 self.sq_task_complete(task)
1871 self.sq_live.remove(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05001872 self.stats.updateActiveSetscene(len(self.sq_live))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001873 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04001874 if status != 0:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001875 self.task_fail(task, status, fakerootlog=fakerootlog)
Brad Bishop96ff1982019-08-19 13:50:42 -04001876 else:
1877 self.task_complete(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001878 return True
1879
1880 def finish_now(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001881 for mc in self.rq.worker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001882 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001883 self.rq.worker[mc].process.stdin.write(b"<finishnow></finishnow>")
1884 self.rq.worker[mc].process.stdin.flush()
1885 except IOError:
1886 # worker must have died?
1887 pass
1888 for mc in self.rq.fakeworker:
1889 try:
1890 self.rq.fakeworker[mc].process.stdin.write(b"<finishnow></finishnow>")
1891 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001892 except IOError:
1893 # worker must have died?
1894 pass
1895
Andrew Geissler595f6302022-01-24 19:11:47 +00001896 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001897 self.rq.state = runQueueFailed
1898 return
1899
1900 self.rq.state = runQueueComplete
1901 return
1902
1903 def finish(self):
1904 self.rq.state = runQueueCleanUp
1905
Andrew Geissler5199d832021-09-24 16:47:35 -05001906 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001907 if active > 0:
1908 bb.event.fire(runQueueExitWait(active), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001909 self.rq.read_workers()
1910 return self.rq.active_fds()
1911
Andrew Geissler595f6302022-01-24 19:11:47 +00001912 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001913 self.rq.state = runQueueFailed
1914 return True
1915
1916 self.rq.state = runQueueComplete
1917 return True
1918
Brad Bishop96ff1982019-08-19 13:50:42 -04001919 # Used by setscene only
1920 def check_dependencies(self, task, taskdeps):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001921 if not self.rq.depvalidate:
1922 return False
1923
Brad Bishop08902b02019-08-20 09:16:51 -04001924 # Must not edit parent data
1925 taskdeps = set(taskdeps)
1926
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001927 taskdata = {}
1928 taskdeps.add(task)
1929 for dep in taskdeps:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001930 (mc, fn, taskname, taskfn) = split_tid_mcfn(dep)
1931 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001932 taskdata[dep] = [pn, taskname, fn]
1933 call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001934 locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001935 valid = bb.utils.better_eval(call, locs)
1936 return valid
1937
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001938 def can_start_task(self):
Andrew Geissler5199d832021-09-24 16:47:35 -05001939 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001940 can_start = active < self.number_tasks
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001941 return can_start
1942
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001943 def get_schedulers(self):
1944 schedulers = set(obj for obj in globals().values()
1945 if type(obj) is type and
1946 issubclass(obj, RunQueueScheduler))
1947
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001948 user_schedulers = self.cfgData.getVar("BB_SCHEDULERS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001949 if user_schedulers:
1950 for sched in user_schedulers.split():
1951 if not "." in sched:
1952 bb.note("Ignoring scheduler '%s' from BB_SCHEDULERS: not an import" % sched)
1953 continue
1954
1955 modname, name = sched.rsplit(".", 1)
1956 try:
1957 module = __import__(modname, fromlist=(name,))
1958 except ImportError as exc:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001959 bb.fatal("Unable to import scheduler '%s' from '%s': %s" % (name, modname, exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001960 else:
1961 schedulers.add(getattr(module, name))
1962 return schedulers
1963
1964 def setbuildable(self, task):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001965 self.runq_buildable.add(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001966 self.sched.newbuildable(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001967
1968 def task_completeoutright(self, task):
1969 """
1970 Mark a task as completed
1971 Look at the reverse dependencies and mark any task with
1972 completed dependencies as buildable
1973 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001974 self.runq_complete.add(task)
1975 for revdep in self.rqdata.runtaskentries[task].revdeps:
1976 if revdep in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001977 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001978 if revdep in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001979 continue
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001980 alldeps = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001981 for dep in self.rqdata.runtaskentries[revdep].depends:
1982 if dep not in self.runq_complete:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001983 alldeps = False
1984 break
1985 if alldeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001986 self.setbuildable(revdep)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001987 logger.debug("Marking task %s as buildable", revdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001988
Andrew Geissler5199d832021-09-24 16:47:35 -05001989 for t in self.sq_deferred.copy():
1990 if self.sq_deferred[t] == task:
1991 logger.debug2("Deferred task %s now buildable" % t)
1992 del self.sq_deferred[t]
1993 update_scenequeue_data([t], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
1994
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001995 def task_complete(self, task):
1996 self.stats.taskCompleted()
1997 bb.event.fire(runQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
1998 self.task_completeoutright(task)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001999 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002000
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002001 def task_fail(self, task, exitcode, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002002 """
2003 Called when a task has failed
2004 Updates the state engine with the failure
2005 """
2006 self.stats.taskFailed()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002007 self.failed_tids.append(task)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002008
Andrew Geissler595f6302022-01-24 19:11:47 +00002009 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002010 if fakerootlog and os.path.exists(fakerootlog):
2011 with open(fakerootlog) as fakeroot_log_file:
2012 fakeroot_failed = False
2013 for line in reversed(fakeroot_log_file.readlines()):
2014 for fakeroot_error in ['mismatch', 'error', 'fatal']:
2015 if fakeroot_error in line.lower():
2016 fakeroot_failed = True
2017 if 'doing new pid setup and server start' in line:
2018 break
Andrew Geissler595f6302022-01-24 19:11:47 +00002019 fakeroot_log.append(line)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002020
2021 if not fakeroot_failed:
Andrew Geissler595f6302022-01-24 19:11:47 +00002022 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002023
Andrew Geissler595f6302022-01-24 19:11:47 +00002024 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 -05002025
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002026 if self.rqdata.taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002027 self.rq.state = runQueueCleanUp
2028
2029 def task_skip(self, task, reason):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002030 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002031 self.setbuildable(task)
2032 bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
2033 self.task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002034 self.stats.taskSkipped()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002035 self.stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002036
Brad Bishop08902b02019-08-20 09:16:51 -04002037 def summarise_scenequeue_errors(self):
2038 err = False
2039 if not self.sqdone:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002040 logger.debug('We could skip tasks %s', "\n".join(sorted(self.scenequeue_covered)))
Andrew Geissler5199d832021-09-24 16:47:35 -05002041 completeevent = sceneQueueComplete(self.stats, self.rq)
Brad Bishop08902b02019-08-20 09:16:51 -04002042 bb.event.fire(completeevent, self.cfgData)
2043 if self.sq_deferred:
2044 logger.error("Scenequeue had deferred entries: %s" % pprint.pformat(self.sq_deferred))
2045 err = True
2046 if self.updated_taskhash_queue:
2047 logger.error("Scenequeue had unprocessed changed taskhash entries: %s" % pprint.pformat(self.updated_taskhash_queue))
2048 err = True
2049 if self.holdoff_tasks:
2050 logger.error("Scenequeue had holdoff tasks: %s" % pprint.pformat(self.holdoff_tasks))
2051 err = True
2052
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002053 for tid in self.scenequeue_covered.intersection(self.scenequeue_notcovered):
2054 # No task should end up in both covered and uncovered, that is a bug.
2055 logger.error("Setscene task %s in both covered and notcovered." % tid)
2056
Brad Bishop08902b02019-08-20 09:16:51 -04002057 for tid in self.rqdata.runq_setscene_tids:
2058 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2059 err = True
2060 logger.error("Setscene Task %s was never marked as covered or not covered" % tid)
2061 if tid not in self.sq_buildable:
2062 err = True
2063 logger.error("Setscene Task %s was never marked as buildable" % tid)
2064 if tid not in self.sq_running:
2065 err = True
2066 logger.error("Setscene Task %s was never marked as running" % tid)
2067
2068 for x in self.rqdata.runtaskentries:
2069 if x not in self.tasks_covered and x not in self.tasks_notcovered:
2070 logger.error("Task %s was never moved from the setscene queue" % x)
2071 err = True
2072 if x not in self.tasks_scenequeue_done:
2073 logger.error("Task %s was never processed by the setscene code" % x)
2074 err = True
Andrew Geissler595f6302022-01-24 19:11:47 +00002075 if not self.rqdata.runtaskentries[x].depends and x not in self.runq_buildable:
Brad Bishop08902b02019-08-20 09:16:51 -04002076 logger.error("Task %s was never marked as buildable by the setscene code" % x)
2077 err = True
2078 return err
2079
2080
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002081 def execute(self):
2082 """
Brad Bishop96ff1982019-08-19 13:50:42 -04002083 Run the tasks in a queue prepared by prepare_runqueue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002084 """
2085
2086 self.rq.read_workers()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002087 if self.updated_taskhash_queue or self.pending_migrations:
2088 self.process_possible_migrations()
2089
2090 if not hasattr(self, "sorted_setscene_tids"):
2091 # Don't want to sort this set every execution
2092 self.sorted_setscene_tids = sorted(self.rqdata.runq_setscene_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002093
Brad Bishop96ff1982019-08-19 13:50:42 -04002094 task = None
2095 if not self.sqdone and self.can_start_task():
2096 # Find the next setscene to run
Andrew Geissler82c905d2020-04-13 13:39:40 -05002097 for nexttask in self.sorted_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002098 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 +00002099 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 -04002100 if nexttask not in self.rqdata.target_tids:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002101 logger.debug2("Skipping setscene for task %s" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002102 self.sq_task_skip(nexttask)
2103 self.scenequeue_notneeded.add(nexttask)
2104 if nexttask in self.sq_deferred:
2105 del self.sq_deferred[nexttask]
2106 return True
Brad Bishop08902b02019-08-20 09:16:51 -04002107 # If covered tasks are running, need to wait for them to complete
2108 for t in self.sqdata.sq_covered_tasks[nexttask]:
2109 if t in self.runq_running and t not in self.runq_complete:
2110 continue
Brad Bishop96ff1982019-08-19 13:50:42 -04002111 if nexttask in self.sq_deferred:
2112 if self.sq_deferred[nexttask] not in self.runq_complete:
2113 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002114 logger.debug("Task %s no longer deferred" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002115 del self.sq_deferred[nexttask]
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002116 valid = self.rq.validate_hashes(set([nexttask]), self.cooker.data, 0, False, summary=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002117 if not valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002118 logger.debug("%s didn't become valid, skipping setscene" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002119 self.sq_task_failoutright(nexttask)
2120 return True
Brad Bishop96ff1982019-08-19 13:50:42 -04002121 if nexttask in self.sqdata.outrightfail:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002122 logger.debug2('No package found, so skipping setscene task %s', nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002123 self.sq_task_failoutright(nexttask)
2124 return True
2125 if nexttask in self.sqdata.unskippable:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002126 logger.debug2("Setscene task %s is unskippable" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002127 task = nexttask
2128 break
2129 if task is not None:
2130 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
2131 taskname = taskname + "_setscene"
2132 if self.rq.check_stamp_task(task, taskname_from_tid(task), recurse = True, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002133 logger.debug2('Stamp for underlying task %s is current, so skipping setscene variant', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002134 self.sq_task_failoutright(task)
2135 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002136
Brad Bishop96ff1982019-08-19 13:50:42 -04002137 if self.cooker.configuration.force:
2138 if task in self.rqdata.target_tids:
2139 self.sq_task_failoutright(task)
2140 return True
2141
2142 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002143 logger.debug2('Setscene stamp current task %s, so skip it and its dependencies', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002144 self.sq_task_skip(task)
2145 return True
2146
2147 if self.cooker.configuration.skipsetscene:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002148 logger.debug2('No setscene tasks should be executed. Skipping %s', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002149 self.sq_task_failoutright(task)
2150 return True
2151
Andrew Geissler5199d832021-09-24 16:47:35 -05002152 startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
Brad Bishop96ff1982019-08-19 13:50:42 -04002153 bb.event.fire(startevent, self.cfgData)
2154
Brad Bishop96ff1982019-08-19 13:50:42 -04002155 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Andrew Geissler517393d2023-01-13 08:55:19 -06002156 runtask = {
2157 'fn' : taskfn,
2158 'task' : task,
2159 'taskname' : taskname,
2160 'taskhash' : self.rqdata.get_task_hash(task),
2161 'unihash' : self.rqdata.get_task_unihash(task),
2162 'quieterrors' : True,
2163 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
2164 'taskdepdata' : self.sq_build_taskdepdata(task),
2165 'dry_run' : False,
2166 'taskdep': taskdep,
2167 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2168 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2169 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2170 }
2171
Brad Bishop96ff1982019-08-19 13:50:42 -04002172 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
2173 if not mc in self.rq.fakeworker:
2174 self.rq.start_fakeworker(self, mc)
Andrew Geissler517393d2023-01-13 08:55:19 -06002175 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002176 self.rq.fakeworker[mc].process.stdin.flush()
2177 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06002178 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002179 self.rq.worker[mc].process.stdin.flush()
2180
Andrew Geissler517393d2023-01-13 08:55:19 -06002181 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002182 self.build_stamps2.append(self.build_stamps[task])
2183 self.sq_running.add(task)
2184 self.sq_live.add(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05002185 self.stats.updateActiveSetscene(len(self.sq_live))
Brad Bishop96ff1982019-08-19 13:50:42 -04002186 if self.can_start_task():
2187 return True
2188
Brad Bishopc68388fc2019-08-26 01:33:31 -04002189 self.update_holdofftasks()
2190
Brad Bishop08902b02019-08-20 09:16:51 -04002191 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 -05002192 hashequiv_logger.verbose("Setscene tasks completed")
Brad Bishop96ff1982019-08-19 13:50:42 -04002193
Brad Bishop08902b02019-08-20 09:16:51 -04002194 err = self.summarise_scenequeue_errors()
Brad Bishop96ff1982019-08-19 13:50:42 -04002195 if err:
2196 self.rq.state = runQueueFailed
2197 return True
2198
2199 if self.cooker.configuration.setsceneonly:
2200 self.rq.state = runQueueComplete
2201 return True
2202 self.sqdone = True
2203
2204 if self.stats.total == 0:
2205 # nothing to do
2206 self.rq.state = runQueueComplete
2207 return True
2208
2209 if self.cooker.configuration.setsceneonly:
2210 task = None
2211 else:
2212 task = self.sched.next()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002213 if task is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002214 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002215
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002216 if self.rqdata.setscene_ignore_tasks is not None:
2217 if self.check_setscene_ignore_tasks(task):
2218 self.task_fail(task, "setscene ignore_tasks")
Brad Bishop96ff1982019-08-19 13:50:42 -04002219 return True
2220
2221 if task in self.tasks_covered:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002222 logger.debug2("Setscene covered task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002223 self.task_skip(task, "covered")
2224 return True
2225
2226 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002227 logger.debug2("Stamp current task %s", task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002228
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002229 self.task_skip(task, "existing")
Andrew Geissler82c905d2020-04-13 13:39:40 -05002230 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002231 return True
2232
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002233 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002234 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2235 startevent = runQueueTaskStarted(task, self.stats, self.rq,
2236 noexec=True)
2237 bb.event.fire(startevent, self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002238 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002239 self.stats.taskActive()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002240 if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Andrew Geissler517393d2023-01-13 08:55:19 -06002241 bb.build.make_stamp_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002242 self.task_complete(task)
2243 return True
2244 else:
2245 startevent = runQueueTaskStarted(task, self.stats, self.rq)
2246 bb.event.fire(startevent, self.cfgData)
2247
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002248 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Andrew Geissler517393d2023-01-13 08:55:19 -06002249 runtask = {
2250 'fn' : taskfn,
2251 'task' : task,
2252 'taskname' : taskname,
2253 'taskhash' : self.rqdata.get_task_hash(task),
2254 'unihash' : self.rqdata.get_task_unihash(task),
2255 'quieterrors' : False,
2256 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
2257 'taskdepdata' : self.build_taskdepdata(task),
2258 'dry_run' : self.rqdata.setscene_enforce,
2259 'taskdep': taskdep,
2260 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2261 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2262 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2263 }
2264
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002265 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 -05002266 if not mc in self.rq.fakeworker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002267 try:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002268 self.rq.start_fakeworker(self, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002269 except OSError as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002270 logger.critical("Failed to spawn fakeroot worker to run %s: %s" % (task, str(exc)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002271 self.rq.state = runQueueFailed
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002272 self.stats.taskFailed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002273 return True
Andrew Geissler517393d2023-01-13 08:55:19 -06002274 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002275 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002276 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06002277 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002278 self.rq.worker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002279
Andrew Geissler517393d2023-01-13 08:55:19 -06002280 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002281 self.build_stamps2.append(self.build_stamps[task])
2282 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002283 self.stats.taskActive()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002284 if self.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002285 return True
2286
Andrew Geissler595f6302022-01-24 19:11:47 +00002287 if self.stats.active > 0 or self.sq_live:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002288 self.rq.read_workers()
2289 return self.rq.active_fds()
2290
Brad Bishop96ff1982019-08-19 13:50:42 -04002291 # No more tasks can be run. If we have deferred setscene tasks we should run them.
2292 if self.sq_deferred:
Patrick Williams92b42cb2022-09-03 06:53:57 -05002293 deferred_tid = list(self.sq_deferred.keys())[0]
2294 blocking_tid = self.sq_deferred.pop(deferred_tid)
Patrick Williams2390b1b2022-11-03 13:47:49 -05002295 logger.warning("Runqueue deadlocked on deferred tasks, forcing task %s blocked by %s" % (deferred_tid, blocking_tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04002296 return True
2297
Andrew Geissler595f6302022-01-24 19:11:47 +00002298 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002299 self.rq.state = runQueueFailed
2300 return True
2301
2302 # Sanity Checks
Brad Bishop08902b02019-08-20 09:16:51 -04002303 err = self.summarise_scenequeue_errors()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002304 for task in self.rqdata.runtaskentries:
2305 if task not in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002306 logger.error("Task %s never buildable!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002307 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002308 elif task not in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002309 logger.error("Task %s never ran!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002310 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002311 elif task not in self.runq_complete:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002312 logger.error("Task %s never completed!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002313 err = True
2314
2315 if err:
2316 self.rq.state = runQueueFailed
2317 else:
2318 self.rq.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002319
2320 return True
2321
Brad Bishopc68388fc2019-08-26 01:33:31 -04002322 def filtermcdeps(self, task, mc, deps):
Andrew Geissler99467da2019-02-25 18:54:23 -06002323 ret = set()
Andrew Geissler99467da2019-02-25 18:54:23 -06002324 for dep in deps:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002325 thismc = mc_from_tid(dep)
2326 if thismc != mc:
Andrew Geissler99467da2019-02-25 18:54:23 -06002327 continue
2328 ret.add(dep)
2329 return ret
2330
Brad Bishopa34c0302019-09-23 22:34:48 -04002331 # We filter out multiconfig dependencies from taskdepdata we pass to the tasks
Andrew Geissler99467da2019-02-25 18:54:23 -06002332 # as most code can't handle them
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002333 def build_taskdepdata(self, task):
2334 taskdepdata = {}
Brad Bishopc68388fc2019-08-26 01:33:31 -04002335 mc = mc_from_tid(task)
Brad Bishop08902b02019-08-20 09:16:51 -04002336 next = self.rqdata.runtaskentries[task].depends.copy()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002337 next.add(task)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002338 next = self.filtermcdeps(task, mc, next)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002339 while next:
2340 additional = []
2341 for revdep in next:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002342 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2343 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2344 deps = self.rqdata.runtaskentries[revdep].depends
2345 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002346 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002347 unihash = self.rqdata.runtaskentries[revdep].unihash
Brad Bishopc68388fc2019-08-26 01:33:31 -04002348 deps = self.filtermcdeps(task, mc, deps)
Brad Bishop19323692019-04-05 15:28:33 -04002349 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002350 for revdep2 in deps:
2351 if revdep2 not in taskdepdata:
2352 additional.append(revdep2)
2353 next = additional
2354
2355 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2356 return taskdepdata
2357
Brad Bishop08902b02019-08-20 09:16:51 -04002358 def update_holdofftasks(self):
Brad Bishopc68388fc2019-08-26 01:33:31 -04002359
2360 if not self.holdoff_need_update:
2361 return
2362
2363 notcovered = set(self.scenequeue_notcovered)
2364 notcovered |= self.cantskip
2365 for tid in self.scenequeue_notcovered:
2366 notcovered |= self.sqdata.sq_covered_tasks[tid]
2367 notcovered |= self.sqdata.unskippable.difference(self.rqdata.runq_setscene_tids)
2368 notcovered.intersection_update(self.tasks_scenequeue_done)
2369
2370 covered = set(self.scenequeue_covered)
2371 for tid in self.scenequeue_covered:
2372 covered |= self.sqdata.sq_covered_tasks[tid]
2373 covered.difference_update(notcovered)
2374 covered.intersection_update(self.tasks_scenequeue_done)
2375
2376 for tid in notcovered | covered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002377 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002378 self.setbuildable(tid)
2379 elif self.rqdata.runtaskentries[tid].depends.issubset(self.runq_complete):
2380 self.setbuildable(tid)
2381
2382 self.tasks_covered = covered
2383 self.tasks_notcovered = notcovered
2384
Brad Bishop08902b02019-08-20 09:16:51 -04002385 self.holdoff_tasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002386
Brad Bishop08902b02019-08-20 09:16:51 -04002387 for tid in self.rqdata.runq_setscene_tids:
2388 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2389 self.holdoff_tasks.add(tid)
2390
2391 for tid in self.holdoff_tasks.copy():
2392 for dep in self.sqdata.sq_covered_tasks[tid]:
2393 if dep not in self.runq_complete:
2394 self.holdoff_tasks.add(dep)
2395
Brad Bishopc68388fc2019-08-26 01:33:31 -04002396 self.holdoff_need_update = False
2397
Brad Bishop08902b02019-08-20 09:16:51 -04002398 def process_possible_migrations(self):
2399
2400 changed = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002401 toprocess = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002402 for tid, unihash in self.updated_taskhash_queue.copy():
2403 if tid in self.runq_running and tid not in self.runq_complete:
2404 continue
2405
2406 self.updated_taskhash_queue.remove((tid, unihash))
2407
2408 if unihash != self.rqdata.runtaskentries[tid].unihash:
Andrew Geisslerc926e172021-05-07 16:11:35 -05002409 # Make sure we rehash any other tasks with the same task hash that we're deferred against.
2410 torehash = [tid]
2411 for deftid in self.sq_deferred:
2412 if self.sq_deferred[deftid] == tid:
2413 torehash.append(deftid)
2414 for hashtid in torehash:
2415 hashequiv_logger.verbose("Task %s unihash changed to %s" % (hashtid, unihash))
2416 self.rqdata.runtaskentries[hashtid].unihash = unihash
2417 bb.parse.siggen.set_unihash(hashtid, unihash)
2418 toprocess.add(hashtid)
Andrew Geissler78b72792022-06-14 06:47:25 -05002419 if torehash:
2420 # Need to save after set_unihash above
2421 bb.parse.siggen.save_unitaskhashes()
Brad Bishop08902b02019-08-20 09:16:51 -04002422
Andrew Geissler82c905d2020-04-13 13:39:40 -05002423 # Work out all tasks which depend upon these
2424 total = set()
2425 next = set()
2426 for p in toprocess:
2427 next |= self.rqdata.runtaskentries[p].revdeps
2428 while next:
2429 current = next.copy()
2430 total = total | next
2431 next = set()
2432 for ntid in current:
2433 next |= self.rqdata.runtaskentries[ntid].revdeps
2434 next.difference_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002435
Andrew Geissler82c905d2020-04-13 13:39:40 -05002436 # Now iterate those tasks in dependency order to regenerate their taskhash/unihash
2437 next = set()
2438 for p in total:
Andrew Geissler595f6302022-01-24 19:11:47 +00002439 if not self.rqdata.runtaskentries[p].depends:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002440 next.add(p)
2441 elif self.rqdata.runtaskentries[p].depends.isdisjoint(total):
2442 next.add(p)
2443
2444 # When an item doesn't have dependencies in total, we can process it. Drop items from total when handled
2445 while next:
2446 current = next.copy()
2447 next = set()
2448 for tid in current:
Andrew Geissler595f6302022-01-24 19:11:47 +00002449 if self.rqdata.runtaskentries[p].depends and not self.rqdata.runtaskentries[tid].depends.isdisjoint(total):
Andrew Geissler82c905d2020-04-13 13:39:40 -05002450 continue
2451 orighash = self.rqdata.runtaskentries[tid].hash
Andrew Geissler517393d2023-01-13 08:55:19 -06002452 newhash = bb.parse.siggen.get_taskhash(tid, self.rqdata.runtaskentries[tid].depends, self.rqdata.dataCaches)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002453 origuni = self.rqdata.runtaskentries[tid].unihash
2454 newuni = bb.parse.siggen.get_unihash(tid)
2455 # FIXME, need to check it can come from sstate at all for determinism?
2456 remapped = False
2457 if newuni == origuni:
2458 # Nothing to do, we match, skip code below
2459 remapped = True
2460 elif tid in self.scenequeue_covered or tid in self.sq_live:
2461 # Already ran this setscene task or it running. Report the new taskhash
2462 bb.parse.siggen.report_unihash_equiv(tid, newhash, origuni, newuni, self.rqdata.dataCaches)
2463 hashequiv_logger.verbose("Already covered setscene for %s so ignoring rehash (remap)" % (tid))
2464 remapped = True
2465
2466 if not remapped:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002467 #logger.debug("Task %s hash changes: %s->%s %s->%s" % (tid, orighash, newhash, origuni, newuni))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002468 self.rqdata.runtaskentries[tid].hash = newhash
2469 self.rqdata.runtaskentries[tid].unihash = newuni
2470 changed.add(tid)
2471
2472 next |= self.rqdata.runtaskentries[tid].revdeps
2473 total.remove(tid)
2474 next.intersection_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002475
2476 if changed:
2477 for mc in self.rq.worker:
2478 self.rq.worker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2479 for mc in self.rq.fakeworker:
2480 self.rq.fakeworker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2481
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002482 hashequiv_logger.debug(pprint.pformat("Tasks changed:\n%s" % (changed)))
Brad Bishop08902b02019-08-20 09:16:51 -04002483
2484 for tid in changed:
2485 if tid not in self.rqdata.runq_setscene_tids:
2486 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002487 if tid not in self.pending_migrations:
2488 self.pending_migrations.add(tid)
2489
Andrew Geissler82c905d2020-04-13 13:39:40 -05002490 update_tasks = []
Brad Bishop08902b02019-08-20 09:16:51 -04002491 for tid in self.pending_migrations.copy():
Andrew Geissler82c905d2020-04-13 13:39:40 -05002492 if tid in self.runq_running or tid in self.sq_live:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002493 # Too late, task already running, not much we can do now
2494 self.pending_migrations.remove(tid)
2495 continue
2496
Brad Bishop08902b02019-08-20 09:16:51 -04002497 valid = True
2498 # Check no tasks this covers are running
2499 for dep in self.sqdata.sq_covered_tasks[tid]:
2500 if dep in self.runq_running and dep not in self.runq_complete:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002501 hashequiv_logger.debug2("Task %s is running which blocks setscene for %s from running" % (dep, tid))
Brad Bishop08902b02019-08-20 09:16:51 -04002502 valid = False
2503 break
2504 if not valid:
2505 continue
2506
2507 self.pending_migrations.remove(tid)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002508 changed = True
Brad Bishop08902b02019-08-20 09:16:51 -04002509
2510 if tid in self.tasks_scenequeue_done:
2511 self.tasks_scenequeue_done.remove(tid)
2512 for dep in self.sqdata.sq_covered_tasks[tid]:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002513 if dep in self.runq_complete and dep not in self.runq_tasksrun:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002514 bb.error("Task %s marked as completed but now needing to rerun? Halting build." % dep)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002515 self.failed_tids.append(tid)
2516 self.rq.state = runQueueCleanUp
2517 return
2518
Brad Bishop08902b02019-08-20 09:16:51 -04002519 if dep not in self.runq_complete:
2520 if dep in self.tasks_scenequeue_done and dep not in self.sqdata.unskippable:
2521 self.tasks_scenequeue_done.remove(dep)
2522
2523 if tid in self.sq_buildable:
2524 self.sq_buildable.remove(tid)
2525 if tid in self.sq_running:
2526 self.sq_running.remove(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002527 if tid in self.sqdata.outrightfail:
2528 self.sqdata.outrightfail.remove(tid)
2529 if tid in self.scenequeue_notcovered:
2530 self.scenequeue_notcovered.remove(tid)
2531 if tid in self.scenequeue_covered:
2532 self.scenequeue_covered.remove(tid)
2533 if tid in self.scenequeue_notneeded:
2534 self.scenequeue_notneeded.remove(tid)
2535
2536 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2537 self.sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2538
2539 if tid in self.stampcache:
2540 del self.stampcache[tid]
2541
2542 if tid in self.build_stamps:
2543 del self.build_stamps[tid]
2544
2545 update_tasks.append(tid)
2546
2547 update_tasks2 = []
2548 for tid in update_tasks:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002549 harddepfail = False
2550 for t in self.sqdata.sq_harddeps:
2551 if tid in self.sqdata.sq_harddeps[t] and t in self.scenequeue_notcovered:
2552 harddepfail = True
2553 break
2554 if not harddepfail and self.sqdata.sq_revdeps[tid].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
Brad Bishop08902b02019-08-20 09:16:51 -04002555 if tid not in self.sq_buildable:
2556 self.sq_buildable.add(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +00002557 if not self.sqdata.sq_revdeps[tid]:
Brad Bishop08902b02019-08-20 09:16:51 -04002558 self.sq_buildable.add(tid)
2559
Andrew Geissler517393d2023-01-13 08:55:19 -06002560 update_tasks2.append((tid, harddepfail, tid in self.sqdata.valid))
Brad Bishop08902b02019-08-20 09:16:51 -04002561
Andrew Geissler517393d2023-01-13 08:55:19 -06002562 if update_tasks2:
Brad Bishop08902b02019-08-20 09:16:51 -04002563 self.sqdone = False
Patrick Williams92b42cb2022-09-03 06:53:57 -05002564 for mc in sorted(self.sqdata.multiconfigs):
Andrew Geissler517393d2023-01-13 08:55:19 -06002565 for tid in sorted([t[0] for t in update_tasks2]):
Patrick Williams92b42cb2022-09-03 06:53:57 -05002566 if mc_from_tid(tid) != mc:
2567 continue
2568 h = pending_hash_index(tid, self.rqdata)
2569 if h in self.sqdata.hashes and tid != self.sqdata.hashes[h]:
2570 self.sq_deferred[tid] = self.sqdata.hashes[h]
2571 bb.note("Deferring %s after %s" % (tid, self.sqdata.hashes[h]))
Andrew Geissler517393d2023-01-13 08:55:19 -06002572 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 -05002573
Andrew Geissler517393d2023-01-13 08:55:19 -06002574 for (tid, harddepfail, origvalid) in update_tasks2:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002575 if tid in self.sqdata.valid and not origvalid:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002576 hashequiv_logger.verbose("Setscene task %s became valid" % tid)
2577 if harddepfail:
Andrew Geissler517393d2023-01-13 08:55:19 -06002578 logger.debug2("%s has an unavailable hard dependency so skipping" % (tid))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002579 self.sq_task_failoutright(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002580
2581 if changed:
Andrew Geissler5199d832021-09-24 16:47:35 -05002582 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002583 self.holdoff_need_update = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002584
Brad Bishop96ff1982019-08-19 13:50:42 -04002585 def scenequeue_updatecounters(self, task, fail=False):
Brad Bishop08902b02019-08-20 09:16:51 -04002586
2587 for dep in sorted(self.sqdata.sq_deps[task]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002588 if fail and task in self.sqdata.sq_harddeps and dep in self.sqdata.sq_harddeps[task]:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002589 if dep in self.scenequeue_covered or dep in self.scenequeue_notcovered:
2590 # dependency could be already processed, e.g. noexec setscene task
2591 continue
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002592 noexec, stamppresent = check_setscene_stamps(dep, self.rqdata, self.rq, self.stampcache)
2593 if noexec or stamppresent:
2594 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002595 logger.debug2("%s was unavailable and is a hard dependency of %s so skipping" % (task, dep))
Brad Bishop96ff1982019-08-19 13:50:42 -04002596 self.sq_task_failoutright(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002597 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002598 if self.sqdata.sq_revdeps[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2599 if dep not in self.sq_buildable:
2600 self.sq_buildable.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002601
Brad Bishop96ff1982019-08-19 13:50:42 -04002602 next = set([task])
2603 while next:
2604 new = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002605 for t in sorted(next):
Brad Bishop96ff1982019-08-19 13:50:42 -04002606 self.tasks_scenequeue_done.add(t)
2607 # Look down the dependency chain for non-setscene things which this task depends on
2608 # and mark as 'done'
2609 for dep in self.rqdata.runtaskentries[t].depends:
2610 if dep in self.rqdata.runq_setscene_tids or dep in self.tasks_scenequeue_done:
2611 continue
2612 if self.rqdata.runtaskentries[dep].revdeps.issubset(self.tasks_scenequeue_done):
2613 new.add(dep)
Brad Bishop96ff1982019-08-19 13:50:42 -04002614 next = new
2615
Andrew Geissler5199d832021-09-24 16:47:35 -05002616 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002617 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002618
2619 def sq_task_completeoutright(self, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002620 """
2621 Mark a task as completed
2622 Look at the reverse dependencies and mark any task with
2623 completed dependencies as buildable
2624 """
2625
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002626 logger.debug('Found task %s which could be accelerated', task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002627 self.scenequeue_covered.add(task)
2628 self.scenequeue_updatecounters(task)
2629
Brad Bishop96ff1982019-08-19 13:50:42 -04002630 def sq_check_taskfail(self, task):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002631 if self.rqdata.setscene_ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002632 realtask = task.split('_setscene')[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002633 (mc, fn, taskname, taskfn) = split_tid_mcfn(realtask)
2634 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002635 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002636 logger.error('Task %s.%s failed' % (pn, taskname + "_setscene"))
2637 self.rq.state = runQueueCleanUp
2638
Brad Bishop96ff1982019-08-19 13:50:42 -04002639 def sq_task_complete(self, task):
Andrew Geissler5199d832021-09-24 16:47:35 -05002640 bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
Brad Bishop96ff1982019-08-19 13:50:42 -04002641 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002642
Brad Bishop96ff1982019-08-19 13:50:42 -04002643 def sq_task_fail(self, task, result):
Andrew Geissler5199d832021-09-24 16:47:35 -05002644 bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002645 self.scenequeue_notcovered.add(task)
2646 self.scenequeue_updatecounters(task, True)
Brad Bishop96ff1982019-08-19 13:50:42 -04002647 self.sq_check_taskfail(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002648
Brad Bishop96ff1982019-08-19 13:50:42 -04002649 def sq_task_failoutright(self, task):
2650 self.sq_running.add(task)
2651 self.sq_buildable.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002652 self.scenequeue_notcovered.add(task)
2653 self.scenequeue_updatecounters(task, True)
2654
Brad Bishop96ff1982019-08-19 13:50:42 -04002655 def sq_task_skip(self, task):
2656 self.sq_running.add(task)
2657 self.sq_buildable.add(task)
2658 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002659
Brad Bishop96ff1982019-08-19 13:50:42 -04002660 def sq_build_taskdepdata(self, task):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002661 def getsetscenedeps(tid):
2662 deps = set()
2663 (mc, fn, taskname, _) = split_tid_mcfn(tid)
2664 realtid = tid + "_setscene"
2665 idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
2666 for (depname, idependtask) in idepends:
2667 if depname not in self.rqdata.taskData[mc].build_targets:
2668 continue
2669
2670 depfn = self.rqdata.taskData[mc].build_targets[depname][0]
2671 if depfn is None:
2672 continue
2673 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2674 deps.add(deptid)
2675 return deps
2676
2677 taskdepdata = {}
2678 next = getsetscenedeps(task)
2679 next.add(task)
2680 while next:
2681 additional = []
2682 for revdep in next:
2683 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2684 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2685 deps = getsetscenedeps(revdep)
2686 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2687 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002688 unihash = self.rqdata.runtaskentries[revdep].unihash
2689 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002690 for revdep2 in deps:
2691 if revdep2 not in taskdepdata:
2692 additional.append(revdep2)
2693 next = additional
2694
2695 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2696 return taskdepdata
2697
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002698 def check_setscene_ignore_tasks(self, tid):
2699 # Check task that is going to run against the ignore tasks list
Brad Bishop96ff1982019-08-19 13:50:42 -04002700 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2701 # Ignore covered tasks
2702 if tid in self.tasks_covered:
2703 return False
2704 # Ignore stamped tasks
2705 if self.rq.check_stamp_task(tid, taskname, cache=self.stampcache):
2706 return False
2707 # Ignore noexec tasks
2708 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2709 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2710 return False
2711
2712 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002713 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Brad Bishop96ff1982019-08-19 13:50:42 -04002714 if tid in self.rqdata.runq_setscene_tids:
Andrew Geissler595f6302022-01-24 19:11:47 +00002715 msg = ['Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)]
Brad Bishop96ff1982019-08-19 13:50:42 -04002716 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002717 msg = ['Task %s.%s attempted to execute unexpectedly' % (pn, taskname)]
Andrew Geissler82c905d2020-04-13 13:39:40 -05002718 for t in self.scenequeue_notcovered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002719 msg.append("\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash))
2720 msg.append('\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered))
2721 logger.error("".join(msg))
Brad Bishop96ff1982019-08-19 13:50:42 -04002722 return True
2723 return False
2724
2725class SQData(object):
2726 def __init__(self):
2727 # SceneQueue dependencies
2728 self.sq_deps = {}
2729 # SceneQueue reverse dependencies
2730 self.sq_revdeps = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002731 # Injected inter-setscene task dependencies
2732 self.sq_harddeps = {}
2733 # Cache of stamp files so duplicates can't run in parallel
2734 self.stamps = {}
2735 # Setscene tasks directly depended upon by the build
2736 self.unskippable = set()
2737 # List of setscene tasks which aren't present
2738 self.outrightfail = set()
2739 # A list of normal tasks a setscene task covers
2740 self.sq_covered_tasks = {}
2741
2742def build_scenequeue_data(sqdata, rqdata, rq, cooker, stampcache, sqrq):
2743
2744 sq_revdeps = {}
2745 sq_revdeps_squash = {}
2746 sq_collated_deps = {}
2747
2748 # We need to construct a dependency graph for the setscene functions. Intermediate
2749 # dependencies between the setscene tasks only complicate the code. This code
2750 # therefore aims to collapse the huge runqueue dependency tree into a smaller one
2751 # only containing the setscene functions.
2752
2753 rqdata.init_progress_reporter.next_stage()
2754
2755 # First process the chains up to the first setscene task.
2756 endpoints = {}
2757 for tid in rqdata.runtaskentries:
2758 sq_revdeps[tid] = copy.copy(rqdata.runtaskentries[tid].revdeps)
2759 sq_revdeps_squash[tid] = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00002760 if not sq_revdeps[tid] and tid not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002761 #bb.warn("Added endpoint %s" % (tid))
2762 endpoints[tid] = set()
2763
2764 rqdata.init_progress_reporter.next_stage()
2765
2766 # Secondly process the chains between setscene tasks.
2767 for tid in rqdata.runq_setscene_tids:
2768 sq_collated_deps[tid] = set()
2769 #bb.warn("Added endpoint 2 %s" % (tid))
2770 for dep in rqdata.runtaskentries[tid].depends:
2771 if tid in sq_revdeps[dep]:
2772 sq_revdeps[dep].remove(tid)
2773 if dep not in endpoints:
2774 endpoints[dep] = set()
2775 #bb.warn(" Added endpoint 3 %s" % (dep))
2776 endpoints[dep].add(tid)
2777
2778 rqdata.init_progress_reporter.next_stage()
2779
2780 def process_endpoints(endpoints):
2781 newendpoints = {}
2782 for point, task in endpoints.items():
2783 tasks = set()
2784 if task:
2785 tasks |= task
2786 if sq_revdeps_squash[point]:
2787 tasks |= sq_revdeps_squash[point]
2788 if point not in rqdata.runq_setscene_tids:
2789 for t in tasks:
2790 sq_collated_deps[t].add(point)
2791 sq_revdeps_squash[point] = set()
2792 if point in rqdata.runq_setscene_tids:
2793 sq_revdeps_squash[point] = tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002794 continue
2795 for dep in rqdata.runtaskentries[point].depends:
2796 if point in sq_revdeps[dep]:
2797 sq_revdeps[dep].remove(point)
2798 if tasks:
2799 sq_revdeps_squash[dep] |= tasks
Andrew Geissler595f6302022-01-24 19:11:47 +00002800 if not sq_revdeps[dep] and dep not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002801 newendpoints[dep] = task
Andrew Geissler595f6302022-01-24 19:11:47 +00002802 if newendpoints:
Brad Bishop96ff1982019-08-19 13:50:42 -04002803 process_endpoints(newendpoints)
2804
2805 process_endpoints(endpoints)
2806
2807 rqdata.init_progress_reporter.next_stage()
2808
Brad Bishop08902b02019-08-20 09:16:51 -04002809 # Build a list of tasks which are "unskippable"
2810 # These are direct endpoints referenced by the build upto and including setscene tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002811 # Take the build endpoints (no revdeps) and find the sstate tasks they depend upon
2812 new = True
2813 for tid in rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00002814 if not rqdata.runtaskentries[tid].revdeps:
Brad Bishop96ff1982019-08-19 13:50:42 -04002815 sqdata.unskippable.add(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002816 sqdata.unskippable |= sqrq.cantskip
Brad Bishop96ff1982019-08-19 13:50:42 -04002817 while new:
2818 new = False
Brad Bishop08902b02019-08-20 09:16:51 -04002819 orig = sqdata.unskippable.copy()
2820 for tid in sorted(orig, reverse=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04002821 if tid in rqdata.runq_setscene_tids:
2822 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00002823 if not rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04002824 # These are tasks which have no setscene tasks in their chain, need to mark as directly buildable
Brad Bishop96ff1982019-08-19 13:50:42 -04002825 sqrq.setbuildable(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002826 sqdata.unskippable |= rqdata.runtaskentries[tid].depends
Brad Bishop08902b02019-08-20 09:16:51 -04002827 if sqdata.unskippable != orig:
2828 new = True
2829
2830 sqrq.tasks_scenequeue_done |= sqdata.unskippable.difference(rqdata.runq_setscene_tids)
Brad Bishop96ff1982019-08-19 13:50:42 -04002831
2832 rqdata.init_progress_reporter.next_stage(len(rqdata.runtaskentries))
2833
2834 # Sanity check all dependencies could be changed to setscene task references
2835 for taskcounter, tid in enumerate(rqdata.runtaskentries):
2836 if tid in rqdata.runq_setscene_tids:
2837 pass
Andrew Geissler595f6302022-01-24 19:11:47 +00002838 elif sq_revdeps_squash[tid]:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002839 bb.msg.fatal("RunQueue", "Something went badly wrong during scenequeue generation, halting. Please report this problem.")
Brad Bishop96ff1982019-08-19 13:50:42 -04002840 else:
2841 del sq_revdeps_squash[tid]
2842 rqdata.init_progress_reporter.update(taskcounter)
2843
2844 rqdata.init_progress_reporter.next_stage()
2845
2846 # Resolve setscene inter-task dependencies
2847 # e.g. do_sometask_setscene[depends] = "targetname:do_someothertask_setscene"
2848 # Note that anything explicitly depended upon will have its reverse dependencies removed to avoid circular dependencies
2849 for tid in rqdata.runq_setscene_tids:
2850 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2851 realtid = tid + "_setscene"
2852 idepends = rqdata.taskData[mc].taskentries[realtid].idepends
Andrew Geissler517393d2023-01-13 08:55:19 -06002853 sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2854
Brad Bishop96ff1982019-08-19 13:50:42 -04002855 for (depname, idependtask) in idepends:
2856
2857 if depname not in rqdata.taskData[mc].build_targets:
2858 continue
2859
2860 depfn = rqdata.taskData[mc].build_targets[depname][0]
2861 if depfn is None:
2862 continue
2863 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2864 if deptid not in rqdata.runtaskentries:
2865 bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (realtid, depfn, idependtask))
2866
2867 if not deptid in sqdata.sq_harddeps:
2868 sqdata.sq_harddeps[deptid] = set()
2869 sqdata.sq_harddeps[deptid].add(tid)
2870
2871 sq_revdeps_squash[tid].add(deptid)
2872 # Have to zero this to avoid circular dependencies
2873 sq_revdeps_squash[deptid] = set()
2874
2875 rqdata.init_progress_reporter.next_stage()
2876
2877 for task in sqdata.sq_harddeps:
2878 for dep in sqdata.sq_harddeps[task]:
2879 sq_revdeps_squash[dep].add(task)
2880
2881 rqdata.init_progress_reporter.next_stage()
2882
2883 #for tid in sq_revdeps_squash:
2884 # data = ""
2885 # for dep in sq_revdeps_squash[tid]:
2886 # data = data + "\n %s" % dep
2887 # bb.warn("Task %s_setscene: is %s " % (tid, data))
2888
2889 sqdata.sq_revdeps = sq_revdeps_squash
Brad Bishop96ff1982019-08-19 13:50:42 -04002890 sqdata.sq_covered_tasks = sq_collated_deps
2891
2892 # Build reverse version of revdeps to populate deps structure
2893 for tid in sqdata.sq_revdeps:
2894 sqdata.sq_deps[tid] = set()
2895 for tid in sqdata.sq_revdeps:
2896 for dep in sqdata.sq_revdeps[tid]:
2897 sqdata.sq_deps[dep].add(tid)
2898
2899 rqdata.init_progress_reporter.next_stage()
2900
Brad Bishop00e122a2019-10-05 11:10:57 -04002901 sqdata.multiconfigs = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002902 for tid in sqdata.sq_revdeps:
Brad Bishop00e122a2019-10-05 11:10:57 -04002903 sqdata.multiconfigs.add(mc_from_tid(tid))
Andrew Geissler595f6302022-01-24 19:11:47 +00002904 if not sqdata.sq_revdeps[tid]:
Brad Bishop96ff1982019-08-19 13:50:42 -04002905 sqrq.sq_buildable.add(tid)
2906
2907 rqdata.init_progress_reporter.finish()
2908
Brad Bishop00e122a2019-10-05 11:10:57 -04002909 sqdata.noexec = set()
2910 sqdata.stamppresent = set()
2911 sqdata.valid = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002912
Patrick Williams213cb262021-08-07 19:21:33 -05002913 sqdata.hashes = {}
2914 sqrq.sq_deferred = {}
2915 for mc in sorted(sqdata.multiconfigs):
2916 for tid in sorted(sqdata.sq_revdeps):
2917 if mc_from_tid(tid) != mc:
2918 continue
2919 h = pending_hash_index(tid, rqdata)
2920 if h not in sqdata.hashes:
2921 sqdata.hashes[h] = tid
2922 else:
2923 sqrq.sq_deferred[tid] = sqdata.hashes[h]
2924 bb.note("Deferring %s after %s" % (tid, sqdata.hashes[h]))
2925
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002926 update_scenequeue_data(sqdata.sq_revdeps, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002927
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002928 # Compute a list of 'stale' sstate tasks where the current hash does not match the one
2929 # in any stamp files. Pass the list out to metadata as an event.
2930 found = {}
2931 for tid in rqdata.runq_setscene_tids:
2932 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002933 stamps = bb.build.find_stale_stamps(taskname, taskfn)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002934 if stamps:
2935 if mc not in found:
2936 found[mc] = {}
2937 found[mc][tid] = stamps
2938 for mc in found:
2939 event = bb.event.StaleSetSceneTasks(found[mc])
2940 bb.event.fire(event, cooker.databuilder.mcdata[mc])
2941
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002942def check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=False):
2943
2944 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2945
2946 taskdep = rqdata.dataCaches[mc].task_deps[taskfn]
2947
2948 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Andrew Geissler517393d2023-01-13 08:55:19 -06002949 bb.build.make_stamp_mcfn(taskname + "_setscene", taskfn)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002950 return True, False
2951
2952 if rq.check_stamp_task(tid, taskname + "_setscene", cache=stampcache):
2953 logger.debug2('Setscene stamp current for task %s', tid)
2954 return False, True
2955
2956 if rq.check_stamp_task(tid, taskname, recurse = True, cache=stampcache):
2957 logger.debug2('Normal stamp current for task %s', tid)
2958 return False, True
2959
2960 return False, False
2961
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002962def update_scenequeue_data(tids, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True):
Brad Bishop00e122a2019-10-05 11:10:57 -04002963
2964 tocheck = set()
2965
2966 for tid in sorted(tids):
2967 if tid in sqdata.stamppresent:
2968 sqdata.stamppresent.remove(tid)
2969 if tid in sqdata.valid:
2970 sqdata.valid.remove(tid)
Andrew Geisslerc926e172021-05-07 16:11:35 -05002971 if tid in sqdata.outrightfail:
2972 sqdata.outrightfail.remove(tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04002973
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002974 noexec, stamppresent = check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002975
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002976 if noexec:
Brad Bishop00e122a2019-10-05 11:10:57 -04002977 sqdata.noexec.add(tid)
2978 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002979 logger.debug2("%s is noexec so skipping setscene" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04002980 continue
2981
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002982 if stamppresent:
Brad Bishop00e122a2019-10-05 11:10:57 -04002983 sqdata.stamppresent.add(tid)
2984 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002985 logger.debug2("%s has a valid stamp, skipping" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04002986 continue
2987
2988 tocheck.add(tid)
2989
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002990 sqdata.valid |= rq.validate_hashes(tocheck, cooker.data, len(sqdata.stamppresent), False, summary=summary)
Brad Bishop00e122a2019-10-05 11:10:57 -04002991
Patrick Williams213cb262021-08-07 19:21:33 -05002992 for tid in tids:
2993 if tid in sqdata.stamppresent:
2994 continue
2995 if tid in sqdata.valid:
2996 continue
2997 if tid in sqdata.noexec:
2998 continue
2999 if tid in sqrq.scenequeue_covered:
3000 continue
3001 if tid in sqrq.scenequeue_notcovered:
3002 continue
3003 if tid in sqrq.sq_deferred:
3004 continue
3005 sqdata.outrightfail.add(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003006 logger.debug2("%s already handled (fallthrough), skipping" % (tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04003007
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003008class TaskFailure(Exception):
3009 """
3010 Exception raised when a task in a runqueue fails
3011 """
3012 def __init__(self, x):
3013 self.args = x
3014
3015
3016class runQueueExitWait(bb.event.Event):
3017 """
3018 Event when waiting for task processes to exit
3019 """
3020
3021 def __init__(self, remain):
3022 self.remain = remain
3023 self.message = "Waiting for %s active tasks to finish" % remain
3024 bb.event.Event.__init__(self)
3025
3026class runQueueEvent(bb.event.Event):
3027 """
3028 Base runQueue event class
3029 """
3030 def __init__(self, task, stats, rq):
3031 self.taskid = task
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003032 self.taskstring = task
3033 self.taskname = taskname_from_tid(task)
3034 self.taskfile = fn_from_tid(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003035 self.taskhash = rq.rqdata.get_task_hash(task)
3036 self.stats = stats.copy()
3037 bb.event.Event.__init__(self)
3038
3039class sceneQueueEvent(runQueueEvent):
3040 """
3041 Base sceneQueue event class
3042 """
3043 def __init__(self, task, stats, rq, noexec=False):
3044 runQueueEvent.__init__(self, task, stats, rq)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003045 self.taskstring = task + "_setscene"
3046 self.taskname = taskname_from_tid(task) + "_setscene"
3047 self.taskfile = fn_from_tid(task)
3048 self.taskhash = rq.rqdata.get_task_hash(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003049
3050class runQueueTaskStarted(runQueueEvent):
3051 """
3052 Event notifying a task was started
3053 """
3054 def __init__(self, task, stats, rq, noexec=False):
3055 runQueueEvent.__init__(self, task, stats, rq)
3056 self.noexec = noexec
3057
3058class sceneQueueTaskStarted(sceneQueueEvent):
3059 """
3060 Event notifying a setscene task was started
3061 """
3062 def __init__(self, task, stats, rq, noexec=False):
3063 sceneQueueEvent.__init__(self, task, stats, rq)
3064 self.noexec = noexec
3065
3066class runQueueTaskFailed(runQueueEvent):
3067 """
3068 Event notifying a task failed
3069 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003070 def __init__(self, task, stats, exitcode, rq, fakeroot_log=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003071 runQueueEvent.__init__(self, task, stats, rq)
3072 self.exitcode = exitcode
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003073 self.fakeroot_log = fakeroot_log
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003074
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003075 def __str__(self):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003076 if self.fakeroot_log:
3077 return "Task (%s) failed with exit code '%s' \nPseudo log:\n%s" % (self.taskstring, self.exitcode, self.fakeroot_log)
3078 else:
3079 return "Task (%s) failed with exit code '%s'" % (self.taskstring, self.exitcode)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003080
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003081class sceneQueueTaskFailed(sceneQueueEvent):
3082 """
3083 Event notifying a setscene task failed
3084 """
3085 def __init__(self, task, stats, exitcode, rq):
3086 sceneQueueEvent.__init__(self, task, stats, rq)
3087 self.exitcode = exitcode
3088
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003089 def __str__(self):
3090 return "Setscene task (%s) failed with exit code '%s' - real task will be run instead" % (self.taskstring, self.exitcode)
3091
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003092class sceneQueueComplete(sceneQueueEvent):
3093 """
3094 Event when all the sceneQueue tasks are complete
3095 """
3096 def __init__(self, stats, rq):
3097 self.stats = stats.copy()
3098 bb.event.Event.__init__(self)
3099
3100class runQueueTaskCompleted(runQueueEvent):
3101 """
3102 Event notifying a task completed
3103 """
3104
3105class sceneQueueTaskCompleted(sceneQueueEvent):
3106 """
3107 Event notifying a setscene task completed
3108 """
3109
3110class runQueueTaskSkipped(runQueueEvent):
3111 """
3112 Event notifying a task was skipped
3113 """
3114 def __init__(self, task, stats, rq, reason):
3115 runQueueEvent.__init__(self, task, stats, rq)
3116 self.reason = reason
3117
Brad Bishop08902b02019-08-20 09:16:51 -04003118class taskUniHashUpdate(bb.event.Event):
3119 """
3120 Base runQueue event class
3121 """
3122 def __init__(self, task, unihash):
3123 self.taskid = task
3124 self.unihash = unihash
3125 bb.event.Event.__init__(self)
3126
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003127class runQueuePipe():
3128 """
3129 Abstraction for a pipe between a worker thread and the server
3130 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003131 def __init__(self, pipein, pipeout, d, rq, rqexec, fakerootlogs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003132 self.input = pipein
3133 if pipeout:
3134 pipeout.close()
3135 bb.utils.nonblockingfd(self.input)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003136 self.queue = b""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003137 self.d = d
3138 self.rq = rq
3139 self.rqexec = rqexec
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003140 self.fakerootlogs = fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003141
3142 def setrunqueueexec(self, rqexec):
3143 self.rqexec = rqexec
3144
3145 def read(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003146 for workers, name in [(self.rq.worker, "Worker"), (self.rq.fakeworker, "Fakeroot")]:
3147 for worker in workers.values():
3148 worker.process.poll()
3149 if worker.process.returncode is not None and not self.rq.teardown:
3150 bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, worker.process.pid, str(worker.process.returncode)))
3151 self.rq.finish_runqueue(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003152
3153 start = len(self.queue)
3154 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003155 self.queue = self.queue + (self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003156 except (OSError, IOError) as e:
3157 if e.errno != errno.EAGAIN:
3158 raise
3159 end = len(self.queue)
3160 found = True
Andrew Geissler595f6302022-01-24 19:11:47 +00003161 while found and self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003162 found = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003163 index = self.queue.find(b"</event>")
3164 while index != -1 and self.queue.startswith(b"<event>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003165 try:
3166 event = pickle.loads(self.queue[7:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003167 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
3168 if isinstance(e, pickle.UnpicklingError) and "truncated" in str(e):
3169 # The pickled data could contain "</event>" so search for the next occurance
3170 # unpickling again, this should be the only way an unpickle error could occur
3171 index = self.queue.find(b"</event>", index + 1)
3172 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003173 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
3174 bb.event.fire_from_worker(event, self.d)
Brad Bishop08902b02019-08-20 09:16:51 -04003175 if isinstance(event, taskUniHashUpdate):
3176 self.rqexec.updated_taskhash_queue.append((event.taskid, event.unihash))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003177 found = True
3178 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003179 index = self.queue.find(b"</event>")
3180 index = self.queue.find(b"</exitcode>")
3181 while index != -1 and self.queue.startswith(b"<exitcode>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003182 try:
3183 task, status = pickle.loads(self.queue[10:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003184 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003185 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003186 (_, _, _, taskfn) = split_tid_mcfn(task)
3187 fakerootlog = None
3188 if self.fakerootlogs and taskfn and taskfn in self.fakerootlogs:
3189 fakerootlog = self.fakerootlogs[taskfn]
3190 self.rqexec.runqueue_process_waitpid(task, status, fakerootlog=fakerootlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003191 found = True
3192 self.queue = self.queue[index+11:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003193 index = self.queue.find(b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003194 return (end > start)
3195
3196 def close(self):
3197 while self.read():
3198 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00003199 if self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003200 print("Warning, worker left partial message: %s" % self.queue)
3201 self.input.close()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003202
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003203def get_setscene_enforce_ignore_tasks(d, targets):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003204 if d.getVar('BB_SETSCENE_ENFORCE') != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003205 return None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003206 ignore_tasks = (d.getVar("BB_SETSCENE_ENFORCE_IGNORE_TASKS") or "").split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003207 outlist = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003208 for item in ignore_tasks[:]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003209 if item.startswith('%:'):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003210 for (mc, target, task, fn) in targets:
3211 outlist.append(target + ':' + item.split(':')[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003212 else:
3213 outlist.append(item)
3214 return outlist
3215
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003216def check_setscene_enforce_ignore_tasks(pn, taskname, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003217 import fnmatch
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003218 if ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003219 item = '%s:%s' % (pn, taskname)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003220 for ignore_tasks in ignore_tasks:
3221 if fnmatch.fnmatch(item, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003222 return True
3223 return False
3224 return True