blob: 02f147454095161c20f3d51ca3beccd6883ebb54 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""
2BitBake 'RunQueue' implementation
3
4Handles preparation and execution of a queue of tasks
5"""
6
7# Copyright (C) 2006-2007 Richard Purdie
8#
Brad Bishopc342db32019-05-15 21:57:59 -04009# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011
12import copy
13import os
14import sys
Patrick Williamsc124f4f2015-09-15 14:41:29 -050015import stat
Patrick Williamsc124f4f2015-09-15 14:41:29 -050016import errno
17import logging
18import re
19import bb
Andrew Geissler82c905d2020-04-13 13:39:40 -050020from bb import msg, event
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021from bb import monitordisk
22import subprocess
Patrick Williamsc0f7c042017-02-23 20:41:17 -060023import pickle
Brad Bishop6e60e8b2018-02-01 10:27:11 -050024from multiprocessing import Process
Brad Bishop19323692019-04-05 15:28:33 -040025import shlex
Brad Bishop96ff1982019-08-19 13:50:42 -040026import pprint
Patrick Williamsdb4c27e2022-08-05 08:10:29 -050027import time
Patrick Williamsc124f4f2015-09-15 14:41:29 -050028
29bblogger = logging.getLogger("BitBake")
30logger = logging.getLogger("BitBake.RunQueue")
Andrew Geissler82c905d2020-04-13 13:39:40 -050031hashequiv_logger = logging.getLogger("BitBake.RunQueue.HashEquiv")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032
Brad Bishop19323692019-04-05 15:28:33 -040033__find_sha256__ = re.compile( r'(?i)(?<![a-z0-9])[a-f0-9]{64}(?![a-z0-9])' )
Patrick Williamsc124f4f2015-09-15 14:41:29 -050034
Patrick Williamsc0f7c042017-02-23 20:41:17 -060035def fn_from_tid(tid):
36 return tid.rsplit(":", 1)[0]
37
38def taskname_from_tid(tid):
39 return tid.rsplit(":", 1)[1]
40
Andrew Geissler99467da2019-02-25 18:54:23 -060041def mc_from_tid(tid):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060042 if tid.startswith('mc:') and tid.count(':') >= 2:
Andrew Geissler99467da2019-02-25 18:54:23 -060043 return tid.split(':')[1]
44 return ""
45
Patrick Williamsc0f7c042017-02-23 20:41:17 -060046def split_tid(tid):
47 (mc, fn, taskname, _) = split_tid_mcfn(tid)
48 return (mc, fn, taskname)
49
Andrew Geissler5a43b432020-06-13 10:46:56 -050050def split_mc(n):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060051 if n.startswith("mc:") and n.count(':') >= 2:
Andrew Geissler5a43b432020-06-13 10:46:56 -050052 _, mc, n = n.split(":", 2)
53 return (mc, n)
54 return ('', n)
55
Patrick Williamsc0f7c042017-02-23 20:41:17 -060056def split_tid_mcfn(tid):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060057 if tid.startswith('mc:') and tid.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060058 elems = tid.split(':')
59 mc = elems[1]
60 fn = ":".join(elems[2:-1])
61 taskname = elems[-1]
Brad Bishop15ae2502019-06-18 21:44:24 -040062 mcfn = "mc:" + mc + ":" + fn
Patrick Williamsc0f7c042017-02-23 20:41:17 -060063 else:
64 tid = tid.rsplit(":", 1)
65 mc = ""
66 fn = tid[0]
67 taskname = tid[1]
68 mcfn = fn
69
70 return (mc, fn, taskname, mcfn)
71
72def build_tid(mc, fn, taskname):
73 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -040074 return "mc:" + mc + ":" + fn + ":" + taskname
Patrick Williamsc0f7c042017-02-23 20:41:17 -060075 return fn + ":" + taskname
76
Brad Bishop96ff1982019-08-19 13:50:42 -040077# Index used to pair up potentially matching multiconfig tasks
78# We match on PN, taskname and hash being equal
79def pending_hash_index(tid, rqdata):
80 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
81 pn = rqdata.dataCaches[mc].pkg_fn[taskfn]
Brad Bishop00e122a2019-10-05 11:10:57 -040082 h = rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -040083 return pn + ":" + "taskname" + h
84
Patrick Williamsc124f4f2015-09-15 14:41:29 -050085class RunQueueStats:
86 """
87 Holds statistics on the tasks handled by the associated runQueue
88 """
Andrew Geissler5199d832021-09-24 16:47:35 -050089 def __init__(self, total, setscene_total):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090 self.completed = 0
91 self.skipped = 0
92 self.failed = 0
93 self.active = 0
Andrew Geissler5199d832021-09-24 16:47:35 -050094 self.setscene_active = 0
95 self.setscene_covered = 0
96 self.setscene_notcovered = 0
97 self.setscene_total = setscene_total
Patrick Williamsc124f4f2015-09-15 14:41:29 -050098 self.total = total
99
100 def copy(self):
Andrew Geissler5199d832021-09-24 16:47:35 -0500101 obj = self.__class__(self.total, self.setscene_total)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102 obj.__dict__.update(self.__dict__)
103 return obj
104
105 def taskFailed(self):
106 self.active = self.active - 1
107 self.failed = self.failed + 1
108
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800109 def taskCompleted(self):
110 self.active = self.active - 1
111 self.completed = self.completed + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500112
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800113 def taskSkipped(self):
114 self.active = self.active + 1
115 self.skipped = self.skipped + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500116
117 def taskActive(self):
118 self.active = self.active + 1
119
Andrew Geissler5199d832021-09-24 16:47:35 -0500120 def updateCovered(self, covered, notcovered):
121 self.setscene_covered = covered
122 self.setscene_notcovered = notcovered
123
124 def updateActiveSetscene(self, active):
125 self.setscene_active = active
126
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127# These values indicate the next step due to be run in the
128# runQueue state machine
129runQueuePrepare = 2
130runQueueSceneInit = 3
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131runQueueRunning = 6
132runQueueFailed = 7
133runQueueCleanUp = 8
134runQueueComplete = 9
135
136class RunQueueScheduler(object):
137 """
138 Control the order tasks are scheduled in.
139 """
140 name = "basic"
141
142 def __init__(self, runqueue, rqdata):
143 """
144 The default scheduler just returns the first buildable task (the
145 priority map is sorted by task number)
146 """
147 self.rq = runqueue
148 self.rqdata = rqdata
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600149 self.numTasks = len(self.rqdata.runtaskentries)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600151 self.prio_map = [self.rqdata.runtaskentries.keys()]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500152
Brad Bishop08902b02019-08-20 09:16:51 -0400153 self.buildable = set()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800154 self.skip_maxthread = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500155 self.stamps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600156 for tid in self.rqdata.runtaskentries:
157 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -0600158 self.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600159 if tid in self.rq.runq_buildable:
160 self.buildable.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500161
162 self.rev_prio_map = None
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500163 self.is_pressure_usable()
164
165 def is_pressure_usable(self):
166 """
167 If monitoring pressure, return True if pressure files can be open and read. For example
168 openSUSE /proc/pressure/* files have readable file permissions but when read the error EOPNOTSUPP (Operation not supported)
169 is returned.
170 """
Patrick Williams92b42cb2022-09-03 06:53:57 -0500171 if self.rq.max_cpu_pressure or self.rq.max_io_pressure or self.rq.max_memory_pressure:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500172 try:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500173 with open("/proc/pressure/cpu") as cpu_pressure_fds, \
174 open("/proc/pressure/io") as io_pressure_fds, \
175 open("/proc/pressure/memory") as memory_pressure_fds:
176
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500177 self.prev_cpu_pressure = cpu_pressure_fds.readline().split()[4].split("=")[1]
178 self.prev_io_pressure = io_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williams92b42cb2022-09-03 06:53:57 -0500179 self.prev_memory_pressure = memory_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500180 self.prev_pressure_time = time.time()
181 self.check_pressure = True
182 except:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500183 bb.note("The /proc/pressure files can't be read. Continuing build without monitoring pressure")
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500184 self.check_pressure = False
185 else:
186 self.check_pressure = False
187
188 def exceeds_max_pressure(self):
189 """
190 Monitor the difference in total pressure at least once per second, if
Patrick Williams92b42cb2022-09-03 06:53:57 -0500191 BB_PRESSURE_MAX_{CPU|IO|MEMORY} are set, return True if above threshold.
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500192 """
193 if self.check_pressure:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500194 with open("/proc/pressure/cpu") as cpu_pressure_fds, \
195 open("/proc/pressure/io") as io_pressure_fds, \
196 open("/proc/pressure/memory") as memory_pressure_fds:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500197 # extract "total" from /proc/pressure/{cpu|io}
198 curr_cpu_pressure = cpu_pressure_fds.readline().split()[4].split("=")[1]
199 curr_io_pressure = io_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williams92b42cb2022-09-03 06:53:57 -0500200 curr_memory_pressure = memory_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500201 now = time.time()
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500202 tdiff = now - self.prev_pressure_time
203 if tdiff > 1.0:
204 exceeds_cpu_pressure = self.rq.max_cpu_pressure and (float(curr_cpu_pressure) - float(self.prev_cpu_pressure)) / tdiff > self.rq.max_cpu_pressure
205 exceeds_io_pressure = self.rq.max_io_pressure and (float(curr_io_pressure) - float(self.prev_io_pressure)) / tdiff > self.rq.max_io_pressure
206 exceeds_memory_pressure = self.rq.max_memory_pressure and (float(curr_memory_pressure) - float(self.prev_memory_pressure)) / tdiff > self.rq.max_memory_pressure
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500207 self.prev_cpu_pressure = curr_cpu_pressure
208 self.prev_io_pressure = curr_io_pressure
Patrick Williams92b42cb2022-09-03 06:53:57 -0500209 self.prev_memory_pressure = curr_memory_pressure
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500210 self.prev_pressure_time = now
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500211 else:
212 exceeds_cpu_pressure = self.rq.max_cpu_pressure and (float(curr_cpu_pressure) - float(self.prev_cpu_pressure)) > self.rq.max_cpu_pressure
213 exceeds_io_pressure = self.rq.max_io_pressure and (float(curr_io_pressure) - float(self.prev_io_pressure)) > self.rq.max_io_pressure
214 exceeds_memory_pressure = self.rq.max_memory_pressure and (float(curr_memory_pressure) - float(self.prev_memory_pressure)) > self.rq.max_memory_pressure
Patrick Williams92b42cb2022-09-03 06:53:57 -0500215 return (exceeds_cpu_pressure or exceeds_io_pressure or exceeds_memory_pressure)
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500216 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500217
218 def next_buildable_task(self):
219 """
220 Return the id of the first task we find that is buildable
221 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500222 # Once tasks are running we don't need to worry about them again
223 self.buildable.difference_update(self.rq.runq_running)
Brad Bishop08902b02019-08-20 09:16:51 -0400224 buildable = set(self.buildable)
Brad Bishop08902b02019-08-20 09:16:51 -0400225 buildable.difference_update(self.rq.holdoff_tasks)
226 buildable.intersection_update(self.rq.tasks_covered | self.rq.tasks_notcovered)
Brad Bishop96ff1982019-08-19 13:50:42 -0400227 if not buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500228 return None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800229
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500230 # Bitbake requires that at least one task be active. Only check for pressure if
231 # this is the case, otherwise the pressure limitation could result in no tasks
232 # being active and no new tasks started thereby, at times, breaking the scheduler.
233 if self.rq.stats.active and self.exceeds_max_pressure():
234 return None
235
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800236 # Filter out tasks that have a max number of threads that have been exceeded
237 skip_buildable = {}
238 for running in self.rq.runq_running.difference(self.rq.runq_complete):
239 rtaskname = taskname_from_tid(running)
240 if rtaskname not in self.skip_maxthread:
241 self.skip_maxthread[rtaskname] = self.rq.cfgData.getVarFlag(rtaskname, "number_threads")
242 if not self.skip_maxthread[rtaskname]:
243 continue
244 if rtaskname in skip_buildable:
245 skip_buildable[rtaskname] += 1
246 else:
247 skip_buildable[rtaskname] = 1
248
Brad Bishop96ff1982019-08-19 13:50:42 -0400249 if len(buildable) == 1:
Brad Bishop08902b02019-08-20 09:16:51 -0400250 tid = buildable.pop()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800251 taskname = taskname_from_tid(tid)
252 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
253 return None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600254 stamp = self.stamps[tid]
255 if stamp not in self.rq.build_stamps.values():
256 return tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500257
258 if not self.rev_prio_map:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600259 self.rev_prio_map = {}
260 for tid in self.rqdata.runtaskentries:
261 self.rev_prio_map[tid] = self.prio_map.index(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262
263 best = None
264 bestprio = None
Brad Bishop96ff1982019-08-19 13:50:42 -0400265 for tid in buildable:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800266 taskname = taskname_from_tid(tid)
267 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
268 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600269 prio = self.rev_prio_map[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500270 if bestprio is None or bestprio > prio:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600271 stamp = self.stamps[tid]
272 if stamp in self.rq.build_stamps.values():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500273 continue
274 bestprio = prio
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600275 best = tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500276
277 return best
278
279 def next(self):
280 """
281 Return the id of the task we should build next
282 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800283 if self.rq.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500284 return self.next_buildable_task()
285
Brad Bishop316dfdd2018-06-25 12:45:53 -0400286 def newbuildable(self, task):
Brad Bishop08902b02019-08-20 09:16:51 -0400287 self.buildable.add(task)
288
289 def removebuildable(self, task):
290 self.buildable.remove(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500291
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500292 def describe_task(self, taskid):
293 result = 'ID %s' % taskid
294 if self.rev_prio_map:
295 result = result + (' pri %d' % self.rev_prio_map[taskid])
296 return result
297
298 def dump_prio(self, comment):
299 bb.debug(3, '%s (most important first):\n%s' %
300 (comment,
301 '\n'.join(['%d. %s' % (index + 1, self.describe_task(taskid)) for
302 index, taskid in enumerate(self.prio_map)])))
303
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500304class RunQueueSchedulerSpeed(RunQueueScheduler):
305 """
306 A scheduler optimised for speed. The priority map is sorted by task weight,
307 heavier weighted tasks (tasks needed by the most other tasks) are run first.
308 """
309 name = "speed"
310
311 def __init__(self, runqueue, rqdata):
312 """
313 The priority map is sorted by task weight.
314 """
315 RunQueueScheduler.__init__(self, runqueue, rqdata)
316
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600317 weights = {}
318 for tid in self.rqdata.runtaskentries:
319 weight = self.rqdata.runtaskentries[tid].weight
320 if not weight in weights:
321 weights[weight] = []
322 weights[weight].append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500323
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600324 self.prio_map = []
325 for weight in sorted(weights):
326 for w in weights[weight]:
327 self.prio_map.append(w)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500328
329 self.prio_map.reverse()
330
331class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed):
332 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500333 A scheduler optimised to complete .bb files as quickly as possible. The
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500334 priority map is sorted by task weight, but then reordered so once a given
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500335 .bb file starts to build, it's completed as quickly as possible by
336 running all tasks related to the same .bb file one after the after.
337 This works well where disk space is at a premium and classes like OE's
338 rm_work are in force.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500339 """
340 name = "completion"
341
342 def __init__(self, runqueue, rqdata):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500343 super(RunQueueSchedulerCompletion, self).__init__(runqueue, rqdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500344
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500345 # Extract list of tasks for each recipe, with tasks sorted
346 # ascending from "must run first" (typically do_fetch) to
347 # "runs last" (do_build). The speed scheduler prioritizes
348 # tasks that must run first before the ones that run later;
349 # this is what we depend on here.
350 task_lists = {}
351 for taskid in self.prio_map:
352 fn, taskname = taskid.rsplit(':', 1)
353 task_lists.setdefault(fn, []).append(taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500354
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500355 # Now unify the different task lists. The strategy is that
356 # common tasks get skipped and new ones get inserted after the
357 # preceeding common one(s) as they are found. Because task
358 # lists should differ only by their number of tasks, but not
359 # the ordering of the common tasks, this should result in a
360 # deterministic result that is a superset of the individual
361 # task ordering.
362 all_tasks = []
363 for recipe, new_tasks in task_lists.items():
364 index = 0
365 old_task = all_tasks[index] if index < len(all_tasks) else None
366 for new_task in new_tasks:
367 if old_task == new_task:
368 # Common task, skip it. This is the fast-path which
369 # avoids a full search.
370 index += 1
371 old_task = all_tasks[index] if index < len(all_tasks) else None
372 else:
373 try:
374 index = all_tasks.index(new_task)
375 # Already present, just not at the current
376 # place. We re-synchronized by changing the
377 # index so that it matches again. Now
378 # move on to the next existing task.
379 index += 1
380 old_task = all_tasks[index] if index < len(all_tasks) else None
381 except ValueError:
382 # Not present. Insert before old_task, which
383 # remains the same (but gets shifted back).
384 all_tasks.insert(index, new_task)
385 index += 1
386 bb.debug(3, 'merged task list: %s' % all_tasks)
387
388 # Now reverse the order so that tasks that finish the work on one
389 # recipe are considered more imporant (= come first). The ordering
390 # is now so that do_build is most important.
391 all_tasks.reverse()
392
393 # Group tasks of the same kind before tasks of less important
394 # kinds at the head of the queue (because earlier = lower
395 # priority number = runs earlier), while preserving the
396 # ordering by recipe. If recipe foo is more important than
397 # bar, then the goal is to work on foo's do_populate_sysroot
398 # before bar's do_populate_sysroot and on the more important
399 # tasks of foo before any of the less important tasks in any
400 # other recipe (if those other recipes are more important than
401 # foo).
402 #
403 # All of this only applies when tasks are runable. Explicit
404 # dependencies still override this ordering by priority.
405 #
406 # Here's an example why this priority re-ordering helps with
407 # minimizing disk usage. Consider a recipe foo with a higher
408 # priority than bar where foo DEPENDS on bar. Then the
409 # implicit rule (from base.bbclass) is that foo's do_configure
410 # depends on bar's do_populate_sysroot. This ensures that
411 # bar's do_populate_sysroot gets done first. Normally the
412 # tasks from foo would continue to run once that is done, and
413 # bar only gets completed and cleaned up later. By ordering
414 # bar's task that depend on bar's do_populate_sysroot before foo's
415 # do_configure, that problem gets avoided.
416 task_index = 0
417 self.dump_prio('original priorities')
418 for task in all_tasks:
419 for index in range(task_index, self.numTasks):
420 taskid = self.prio_map[index]
421 taskname = taskid.rsplit(':', 1)[1]
422 if taskname == task:
423 del self.prio_map[index]
424 self.prio_map.insert(task_index, taskid)
425 task_index += 1
426 self.dump_prio('completion priorities')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500427
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600428class RunTaskEntry(object):
429 def __init__(self):
430 self.depends = set()
431 self.revdeps = set()
432 self.hash = None
Brad Bishop19323692019-04-05 15:28:33 -0400433 self.unihash = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600434 self.task = None
435 self.weight = 1
436
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500437class RunQueueData:
438 """
439 BitBake Run Queue implementation
440 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600441 def __init__(self, rq, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500442 self.cooker = cooker
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600443 self.dataCaches = dataCaches
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444 self.taskData = taskData
445 self.targets = targets
446 self.rq = rq
447 self.warn_multi_bb = False
448
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000449 self.multi_provider_allowed = (cfgData.getVar("BB_MULTI_PROVIDER_ALLOWED") or "").split()
450 self.setscene_ignore_tasks = get_setscene_enforce_ignore_tasks(cfgData, targets)
451 self.setscene_ignore_tasks_checked = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500452 self.setscene_enforce = (cfgData.getVar('BB_SETSCENE_ENFORCE') == "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600453 self.init_progress_reporter = bb.progress.DummyMultiStageProcessProgressReporter()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500454
455 self.reset()
456
457 def reset(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600458 self.runtaskentries = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500459
460 def runq_depends_names(self, ids):
461 import re
462 ret = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600463 for id in ids:
464 nam = os.path.basename(id)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500465 nam = re.sub("_[^,]*,", ",", nam)
466 ret.extend([nam])
467 return ret
468
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600469 def get_task_hash(self, tid):
470 return self.runtaskentries[tid].hash
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471
Brad Bishop19323692019-04-05 15:28:33 -0400472 def get_task_unihash(self, tid):
473 return self.runtaskentries[tid].unihash
474
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600475 def get_user_idstring(self, tid, task_name_suffix = ""):
476 return tid + task_name_suffix
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500477
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500478 def get_short_user_idstring(self, task, task_name_suffix = ""):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500479 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
480 pn = self.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600481 taskname = taskname_from_tid(task) + task_name_suffix
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500482 return "%s:%s" % (pn, taskname)
483
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500484 def circular_depchains_handler(self, tasks):
485 """
486 Some tasks aren't buildable, likely due to circular dependency issues.
487 Identify the circular dependencies and print them in a user readable format.
488 """
489 from copy import deepcopy
490
491 valid_chains = []
492 explored_deps = {}
493 msgs = []
494
Andrew Geissler99467da2019-02-25 18:54:23 -0600495 class TooManyLoops(Exception):
496 pass
497
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500498 def chain_reorder(chain):
499 """
500 Reorder a dependency chain so the lowest task id is first
501 """
502 lowest = 0
503 new_chain = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600504 for entry in range(len(chain)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500505 if chain[entry] < chain[lowest]:
506 lowest = entry
507 new_chain.extend(chain[lowest:])
508 new_chain.extend(chain[:lowest])
509 return new_chain
510
511 def chain_compare_equal(chain1, chain2):
512 """
513 Compare two dependency chains and see if they're the same
514 """
515 if len(chain1) != len(chain2):
516 return False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600517 for index in range(len(chain1)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500518 if chain1[index] != chain2[index]:
519 return False
520 return True
521
522 def chain_array_contains(chain, chain_array):
523 """
524 Return True if chain_array contains chain
525 """
526 for ch in chain_array:
527 if chain_compare_equal(ch, chain):
528 return True
529 return False
530
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600531 def find_chains(tid, prev_chain):
532 prev_chain.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500533 total_deps = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600534 total_deps.extend(self.runtaskentries[tid].revdeps)
535 for revdep in self.runtaskentries[tid].revdeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500536 if revdep in prev_chain:
537 idx = prev_chain.index(revdep)
538 # To prevent duplicates, reorder the chain to start with the lowest taskid
539 # and search through an array of those we've already printed
540 chain = prev_chain[idx:]
541 new_chain = chain_reorder(chain)
542 if not chain_array_contains(new_chain, valid_chains):
543 valid_chains.append(new_chain)
544 msgs.append("Dependency loop #%d found:\n" % len(valid_chains))
545 for dep in new_chain:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600546 msgs.append(" Task %s (dependent Tasks %s)\n" % (dep, self.runq_depends_names(self.runtaskentries[dep].depends)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500547 msgs.append("\n")
548 if len(valid_chains) > 10:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000549 msgs.append("Halted dependency loops search after 10 matches.\n")
Andrew Geissler99467da2019-02-25 18:54:23 -0600550 raise TooManyLoops
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500551 continue
552 scan = False
553 if revdep not in explored_deps:
554 scan = True
555 elif revdep in explored_deps[revdep]:
556 scan = True
557 else:
558 for dep in prev_chain:
559 if dep in explored_deps[revdep]:
560 scan = True
561 if scan:
562 find_chains(revdep, copy.deepcopy(prev_chain))
563 for dep in explored_deps[revdep]:
564 if dep not in total_deps:
565 total_deps.append(dep)
566
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600567 explored_deps[tid] = total_deps
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500568
Andrew Geissler99467da2019-02-25 18:54:23 -0600569 try:
570 for task in tasks:
571 find_chains(task, [])
572 except TooManyLoops:
573 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500574
575 return msgs
576
577 def calculate_task_weights(self, endpoints):
578 """
579 Calculate a number representing the "weight" of each task. Heavier weighted tasks
580 have more dependencies and hence should be executed sooner for maximum speed.
581
582 This function also sanity checks the task list finding tasks that are not
583 possible to execute due to circular dependencies.
584 """
585
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600586 numTasks = len(self.runtaskentries)
587 weight = {}
588 deps_left = {}
589 task_done = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500590
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600591 for tid in self.runtaskentries:
592 task_done[tid] = False
593 weight[tid] = 1
594 deps_left[tid] = len(self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500595
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600596 for tid in endpoints:
597 weight[tid] = 10
598 task_done[tid] = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500599
600 while True:
601 next_points = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600602 for tid in endpoints:
603 for revdep in self.runtaskentries[tid].depends:
604 weight[revdep] = weight[revdep] + weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500605 deps_left[revdep] = deps_left[revdep] - 1
606 if deps_left[revdep] == 0:
607 next_points.append(revdep)
608 task_done[revdep] = True
609 endpoints = next_points
Andrew Geissler595f6302022-01-24 19:11:47 +0000610 if not next_points:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500611 break
612
613 # Circular dependency sanity check
614 problem_tasks = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600615 for tid in self.runtaskentries:
616 if task_done[tid] is False or deps_left[tid] != 0:
617 problem_tasks.append(tid)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600618 logger.debug2("Task %s is not buildable", tid)
619 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 -0600620 self.runtaskentries[tid].weight = weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500621
622 if problem_tasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600623 message = "%s unbuildable tasks were found.\n" % len(problem_tasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500624 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"
625 message = message + "Identifying dependency loops (this may take a short while)...\n"
626 logger.error(message)
627
628 msgs = self.circular_depchains_handler(problem_tasks)
629
630 message = "\n"
631 for msg in msgs:
632 message = message + msg
633 bb.msg.fatal("RunQueue", message)
634
635 return weight
636
637 def prepare(self):
638 """
639 Turn a set of taskData into a RunQueue and compute data needed
640 to optimise the execution order.
641 """
642
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600643 runq_build = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500644 recursivetasks = {}
645 recursiveitasks = {}
646 recursivetasksselfref = set()
647
648 taskData = self.taskData
649
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600650 found = False
651 for mc in self.taskData:
Andrew Geissler595f6302022-01-24 19:11:47 +0000652 if taskData[mc].taskentries:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600653 found = True
654 break
655 if not found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656 # Nothing to do
657 return 0
658
Andrew Geissler517393d2023-01-13 08:55:19 -0600659 bb.parse.siggen.setup_datacache(self.dataCaches)
660
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600661 self.init_progress_reporter.start()
662 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600663 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500664
665 # Step A - Work out a list of tasks to run
666 #
667 # Taskdata gives us a list of possible providers for every build and run
668 # target ordered by priority. It also gives information on each of those
669 # providers.
670 #
671 # To create the actual list of tasks to execute we fix the list of
672 # providers and then resolve the dependencies into task IDs. This
673 # process is repeated for each type of dependency (tdepends, deptask,
674 # rdeptast, recrdeptask, idepends).
675
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600676 def add_build_dependencies(depids, tasknames, depends, mc):
677 for depname in depids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500678 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600679 if depname not in taskData[mc].build_targets or not taskData[mc].build_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500680 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600681 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500682 if depdata is None:
683 continue
684 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600685 t = depdata + ":" + taskname
686 if t in taskData[mc].taskentries:
687 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500688
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600689 def add_runtime_dependencies(depids, tasknames, depends, mc):
690 for depname in depids:
691 if depname not in taskData[mc].run_targets or not taskData[mc].run_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500692 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600693 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500694 if depdata is None:
695 continue
696 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600697 t = depdata + ":" + taskname
698 if t in taskData[mc].taskentries:
699 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500700
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800701 def add_mc_dependencies(mc, tid):
702 mcdeps = taskData[mc].get_mcdepends()
703 for dep in mcdeps:
704 mcdependency = dep.split(':')
705 pn = mcdependency[3]
706 frommc = mcdependency[1]
707 mcdep = mcdependency[2]
708 deptask = mcdependency[4]
Andrew Geissler517393d2023-01-13 08:55:19 -0600709 if mcdep not in taskData:
710 bb.fatal("Multiconfig '%s' is referenced in multiconfig dependency '%s' but not enabled in BBMULTICONFIG?" % (mcdep, dep))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800711 if mc == frommc:
712 fn = taskData[mcdep].build_targets[pn][0]
713 newdep = '%s:%s' % (fn,deptask)
714 taskData[mc].taskentries[tid].tdepends.append(newdep)
715
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600716 for mc in taskData:
717 for tid in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500718
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600719 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
720 #runtid = build_tid(mc, fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500721
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600722 #logger.debug2("Processing %s,%s:%s", mc, fn, taskname)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723
724 depends = set()
725 task_deps = self.dataCaches[mc].task_deps[taskfn]
726
727 self.runtaskentries[tid] = RunTaskEntry()
728
729 if fn in taskData[mc].failed_fns:
730 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800732 # We add multiconfig dependencies before processing internal task deps (tdepends)
733 if 'mcdepends' in task_deps and taskname in task_deps['mcdepends']:
734 add_mc_dependencies(mc, tid)
735
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736 # Resolve task internal dependencies
737 #
738 # e.g. addtask before X after Y
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600739 for t in taskData[mc].taskentries[tid].tdepends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800740 (depmc, depfn, deptaskname, _) = split_tid_mcfn(t)
741 depends.add(build_tid(depmc, depfn, deptaskname))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500742
743 # Resolve 'deptask' dependencies
744 #
745 # e.g. do_sometask[deptask] = "do_someothertask"
746 # (makes sure sometask runs after someothertask of all DEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600747 if 'deptask' in task_deps and taskname in task_deps['deptask']:
748 tasknames = task_deps['deptask'][taskname].split()
749 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500750
751 # Resolve 'rdeptask' dependencies
752 #
753 # e.g. do_sometask[rdeptask] = "do_someothertask"
754 # (makes sure sometask runs after someothertask of all RDEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600755 if 'rdeptask' in task_deps and taskname in task_deps['rdeptask']:
756 tasknames = task_deps['rdeptask'][taskname].split()
757 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500758
759 # Resolve inter-task dependencies
760 #
761 # e.g. do_sometask[depends] = "targetname:do_someothertask"
762 # (makes sure sometask runs after targetname's someothertask)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600763 idepends = taskData[mc].taskentries[tid].idepends
764 for (depname, idependtask) in idepends:
765 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 -0500766 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600767 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500768 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600769 t = depdata + ":" + idependtask
770 depends.add(t)
771 if t not in taskData[mc].taskentries:
772 bb.msg.fatal("RunQueue", "Task %s in %s depends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
773 irdepends = taskData[mc].taskentries[tid].irdepends
774 for (depname, idependtask) in irdepends:
775 if depname in taskData[mc].run_targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500776 # Won't be in run_targets if ASSUME_PROVIDED
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500777 if not taskData[mc].run_targets[depname]:
778 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600779 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500780 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600781 t = depdata + ":" + idependtask
782 depends.add(t)
783 if t not in taskData[mc].taskentries:
784 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 -0500785
786 # Resolve recursive 'recrdeptask' dependencies (Part A)
787 #
788 # e.g. do_sometask[recrdeptask] = "do_someothertask"
789 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
790 # We cover the recursive part of the dependencies below
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600791 if 'recrdeptask' in task_deps and taskname in task_deps['recrdeptask']:
792 tasknames = task_deps['recrdeptask'][taskname].split()
793 recursivetasks[tid] = tasknames
794 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
795 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
796 if taskname in tasknames:
797 recursivetasksselfref.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500798
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600799 if 'recideptask' in task_deps and taskname in task_deps['recideptask']:
800 recursiveitasks[tid] = []
801 for t in task_deps['recideptask'][taskname].split():
802 newdep = build_tid(mc, fn, t)
803 recursiveitasks[tid].append(newdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500804
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600805 self.runtaskentries[tid].depends = depends
Brad Bishop316dfdd2018-06-25 12:45:53 -0400806 # Remove all self references
807 self.runtaskentries[tid].depends.discard(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500808
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600809 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500810
Brad Bishop316dfdd2018-06-25 12:45:53 -0400811 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600812 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400813
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500814 # Resolve recursive 'recrdeptask' dependencies (Part B)
815 #
816 # e.g. do_sometask[recrdeptask] = "do_someothertask"
817 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600818 # 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 -0600819
Brad Bishop316dfdd2018-06-25 12:45:53 -0400820 # Generating/interating recursive lists of dependencies is painful and potentially slow
821 # Precompute recursive task dependencies here by:
822 # a) create a temp list of reverse dependencies (revdeps)
823 # b) walk up the ends of the chains (when a given task no longer has dependencies i.e. len(deps) == 0)
824 # c) combine the total list of dependencies in cumulativedeps
825 # d) optimise by pre-truncating 'task' off the items in cumulativedeps (keeps items in sets lower)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500826
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500827
Brad Bishop316dfdd2018-06-25 12:45:53 -0400828 revdeps = {}
829 deps = {}
830 cumulativedeps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600831 for tid in self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400832 deps[tid] = set(self.runtaskentries[tid].depends)
833 revdeps[tid] = set()
834 cumulativedeps[tid] = set()
835 # Generate a temp list of reverse dependencies
836 for tid in self.runtaskentries:
837 for dep in self.runtaskentries[tid].depends:
838 revdeps[dep].add(tid)
839 # Find the dependency chain endpoints
840 endpoints = set()
841 for tid in self.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +0000842 if not deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400843 endpoints.add(tid)
844 # Iterate the chains collating dependencies
845 while endpoints:
846 next = set()
847 for tid in endpoints:
848 for dep in revdeps[tid]:
849 cumulativedeps[dep].add(fn_from_tid(tid))
850 cumulativedeps[dep].update(cumulativedeps[tid])
851 if tid in deps[dep]:
852 deps[dep].remove(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +0000853 if not deps[dep]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400854 next.add(dep)
855 endpoints = next
856 #for tid in deps:
Andrew Geissler595f6302022-01-24 19:11:47 +0000857 # if deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400858 # bb.warn("Sanity test failure, dependencies left for %s (%s)" % (tid, deps[tid]))
859
860 # Loop here since recrdeptasks can depend upon other recrdeptasks and we have to
861 # resolve these recursively until we aren't adding any further extra dependencies
862 extradeps = True
863 while extradeps:
864 extradeps = 0
865 for tid in recursivetasks:
866 tasknames = recursivetasks[tid]
867
868 totaldeps = set(self.runtaskentries[tid].depends)
869 if tid in recursiveitasks:
870 totaldeps.update(recursiveitasks[tid])
871 for dep in recursiveitasks[tid]:
872 if dep not in self.runtaskentries:
873 continue
874 totaldeps.update(self.runtaskentries[dep].depends)
875
876 deps = set()
877 for dep in totaldeps:
878 if dep in cumulativedeps:
879 deps.update(cumulativedeps[dep])
880
881 for t in deps:
882 for taskname in tasknames:
883 newtid = t + ":" + taskname
884 if newtid == tid:
885 continue
886 if newtid in self.runtaskentries and newtid not in self.runtaskentries[tid].depends:
887 extradeps += 1
888 self.runtaskentries[tid].depends.add(newtid)
889
890 # Handle recursive tasks which depend upon other recursive tasks
891 deps = set()
892 for dep in self.runtaskentries[tid].depends.intersection(recursivetasks):
893 deps.update(self.runtaskentries[dep].depends.difference(self.runtaskentries[tid].depends))
894 for newtid in deps:
895 for taskname in tasknames:
896 if not newtid.endswith(":" + taskname):
897 continue
898 if newtid in self.runtaskentries:
899 extradeps += 1
900 self.runtaskentries[tid].depends.add(newtid)
901
902 bb.debug(1, "Added %s recursive dependencies in this loop" % extradeps)
903
904 # Remove recrdeptask circular references so that do_a[recrdeptask] = "do_a do_b" can work
905 for tid in recursivetasksselfref:
906 self.runtaskentries[tid].depends.difference_update(recursivetasksselfref)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600907
908 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600909 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600910
911 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500912
913 # Step B - Mark all active tasks
914 #
915 # Start with the tasks we were asked to run and mark all dependencies
916 # as active too. If the task is to be 'forced', clear its stamp. Once
917 # all active tasks are marked, prune the ones we don't need.
918
919 logger.verbose("Marking Active Tasks")
920
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600921 def mark_active(tid, depth):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500922 """
923 Mark an item as active along with its depends
924 (calls itself recursively)
925 """
926
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600927 if tid in runq_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500928 return
929
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600930 runq_build[tid] = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500931
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600932 depends = self.runtaskentries[tid].depends
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500933 for depend in depends:
934 mark_active(depend, depth+1)
935
Brad Bishop79641f22019-09-10 07:20:22 -0400936 def invalidate_task(tid, error_nostamp):
937 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
938 taskdep = self.dataCaches[mc].task_deps[taskfn]
939 if fn + ":" + taskname not in taskData[mc].taskentries:
940 logger.warning("Task %s does not exist, invalidating this task will have no effect" % taskname)
941 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
942 if error_nostamp:
943 bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
944 else:
945 bb.debug(1, "Task %s is marked nostamp, cannot invalidate this task" % taskname)
946 else:
947 logger.verbose("Invalidate task %s, %s", taskname, fn)
Andrew Geissler517393d2023-01-13 08:55:19 -0600948 bb.parse.siggen.invalidate_task(taskname, taskfn)
Brad Bishop79641f22019-09-10 07:20:22 -0400949
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600950 self.target_tids = []
951 for (mc, target, task, fn) in self.targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500952
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600953 if target not in taskData[mc].build_targets or not taskData[mc].build_targets[target]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500954 continue
955
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600956 if target in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500957 continue
958
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500959 parents = False
960 if task.endswith('-'):
961 parents = True
962 task = task[:-1]
963
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600964 if fn in taskData[mc].failed_fns:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500965 continue
966
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600967 # fn already has mc prefix
968 tid = fn + ":" + task
969 self.target_tids.append(tid)
970 if tid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500971 import difflib
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600972 tasks = []
973 for x in taskData[mc].taskentries:
974 if x.startswith(fn + ":"):
975 tasks.append(taskname_from_tid(x))
976 close_matches = difflib.get_close_matches(task, tasks, cutoff=0.7)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500977 if close_matches:
978 extra = ". Close matches:\n %s" % "\n ".join(close_matches)
979 else:
980 extra = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600981 bb.msg.fatal("RunQueue", "Task %s does not exist for target %s (%s)%s" % (task, target, tid, extra))
982
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500983 # For tasks called "XXXX-", ony run their dependencies
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500984 if parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600985 for i in self.runtaskentries[tid].depends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500986 mark_active(i, 1)
987 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600988 mark_active(tid, 1)
989
990 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600991 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500992
993 # Step C - Prune all inactive tasks
994 #
995 # Once all active tasks are marked, prune the ones we don't need.
996
Brad Bishop316dfdd2018-06-25 12:45:53 -0400997 # Handle --runall
998 if self.cooker.configuration.runall:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500999 # re-run the mark_active and then drop unused tasks from new list
Andrew Geissler595f6302022-01-24 19:11:47 +00001000 reduced_tasklist = set(self.runtaskentries.keys())
1001 for tid in list(self.runtaskentries.keys()):
1002 if tid not in runq_build:
1003 reduced_tasklist.remove(tid)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001004 runq_build = {}
Brad Bishop316dfdd2018-06-25 12:45:53 -04001005
1006 for task in self.cooker.configuration.runall:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001007 if not task.startswith("do_"):
1008 task = "do_{0}".format(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001009 runall_tids = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00001010 for tid in reduced_tasklist:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001011 wanttid = "{0}:{1}".format(fn_from_tid(tid), task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001012 if wanttid in self.runtaskentries:
1013 runall_tids.add(wanttid)
1014
1015 for tid in list(runall_tids):
Andrew Geissler595f6302022-01-24 19:11:47 +00001016 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001017 if self.cooker.configuration.force:
1018 invalidate_task(tid, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001019
Andrew Geissler595f6302022-01-24 19:11:47 +00001020 delcount = set()
1021 for tid in list(self.runtaskentries.keys()):
1022 if tid not in runq_build:
1023 delcount.add(tid)
1024 del self.runtaskentries[tid]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001025
Andrew Geissler595f6302022-01-24 19:11:47 +00001026 if self.cooker.configuration.runall:
1027 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001028 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)))
1029
1030 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001031 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001032
1033 # Handle runonly
1034 if self.cooker.configuration.runonly:
1035 # re-run the mark_active and then drop unused tasks from new list
1036 runq_build = {}
1037
1038 for task in self.cooker.configuration.runonly:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001039 if not task.startswith("do_"):
1040 task = "do_{0}".format(task)
Andrew Geissler595f6302022-01-24 19:11:47 +00001041 runonly_tids = [k for k in self.runtaskentries.keys() if taskname_from_tid(k) == task]
Brad Bishop316dfdd2018-06-25 12:45:53 -04001042
Andrew Geissler595f6302022-01-24 19:11:47 +00001043 for tid in runonly_tids:
1044 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001045 if self.cooker.configuration.force:
1046 invalidate_task(tid, False)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001047
1048 for tid in list(self.runtaskentries.keys()):
1049 if tid not in runq_build:
Andrew Geissler595f6302022-01-24 19:11:47 +00001050 delcount.add(tid)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001051 del self.runtaskentries[tid]
1052
Andrew Geissler595f6302022-01-24 19:11:47 +00001053 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001054 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 -05001055
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001056 #
1057 # Step D - Sanity checks and computation
1058 #
1059
1060 # Check to make sure we still have tasks to run
Andrew Geissler595f6302022-01-24 19:11:47 +00001061 if not self.runtaskentries:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001062 if not taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001063 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.")
1064 else:
1065 bb.msg.fatal("RunQueue", "No active tasks and not in --continue mode?! Please report this bug.")
1066
Brad Bishop316dfdd2018-06-25 12:45:53 -04001067 logger.verbose("Pruned %s inactive tasks, %s left", len(delcount), len(self.runtaskentries))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001068
1069 logger.verbose("Assign Weightings")
1070
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001071 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001072 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001073
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001074 # Generate a list of reverse dependencies to ease future calculations
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001075 for tid in self.runtaskentries:
1076 for dep in self.runtaskentries[tid].depends:
1077 self.runtaskentries[dep].revdeps.add(tid)
1078
1079 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001080 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001081
1082 # Identify tasks at the end of dependency chains
1083 # Error on circular dependency loops (length two)
1084 endpoints = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001085 for tid in self.runtaskentries:
1086 revdeps = self.runtaskentries[tid].revdeps
Andrew Geissler595f6302022-01-24 19:11:47 +00001087 if not revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001088 endpoints.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001089 for dep in revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001090 if dep in self.runtaskentries[tid].depends:
1091 bb.msg.fatal("RunQueue", "Task %s has circular dependency on %s" % (tid, dep))
1092
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001093
1094 logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
1095
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001096 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001097 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001098
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001099 # Calculate task weights
1100 # Check of higher length circular dependencies
1101 self.runq_weight = self.calculate_task_weights(endpoints)
1102
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001103 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001104 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001105
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001106 # Sanity Check - Check for multiple tasks building the same provider
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001107 for mc in self.dataCaches:
1108 prov_list = {}
1109 seen_fn = []
1110 for tid in self.runtaskentries:
1111 (tidmc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1112 if taskfn in seen_fn:
1113 continue
1114 if mc != tidmc:
1115 continue
1116 seen_fn.append(taskfn)
1117 for prov in self.dataCaches[mc].fn_provides[taskfn]:
1118 if prov not in prov_list:
1119 prov_list[prov] = [taskfn]
1120 elif taskfn not in prov_list[prov]:
1121 prov_list[prov].append(taskfn)
1122 for prov in prov_list:
1123 if len(prov_list[prov]) < 2:
1124 continue
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001125 if prov in self.multi_provider_allowed:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001126 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001127 seen_pn = []
1128 # If two versions of the same PN are being built its fatal, we don't support it.
1129 for fn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001130 pn = self.dataCaches[mc].pkg_fn[fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001131 if pn not in seen_pn:
1132 seen_pn.append(pn)
1133 else:
1134 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 +00001135 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 -05001136 #
1137 # Construct a list of things which uniquely depend on each provider
1138 # since this may help the user figure out which dependency is triggering this warning
1139 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001140 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 -05001141 deplist = {}
1142 commondeps = None
1143 for provfn in prov_list[prov]:
1144 deps = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001145 for tid in self.runtaskentries:
1146 fn = fn_from_tid(tid)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001147 if fn != provfn:
1148 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001149 for dep in self.runtaskentries[tid].revdeps:
1150 fn = fn_from_tid(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001151 if fn == provfn:
1152 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001153 deps.add(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001154 if not commondeps:
1155 commondeps = set(deps)
1156 else:
1157 commondeps &= deps
1158 deplist[provfn] = deps
1159 for provfn in deplist:
Andrew Geissler595f6302022-01-24 19:11:47 +00001160 msgs.append("\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001161 #
1162 # Construct a list of provides and runtime providers for each recipe
1163 # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC)
1164 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001165 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 -05001166 provide_results = {}
1167 rprovide_results = {}
1168 commonprovs = None
1169 commonrprovs = None
1170 for provfn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001171 provides = set(self.dataCaches[mc].fn_provides[provfn])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001172 rprovides = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001173 for rprovide in self.dataCaches[mc].rproviders:
1174 if provfn in self.dataCaches[mc].rproviders[rprovide]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001175 rprovides.add(rprovide)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001176 for package in self.dataCaches[mc].packages:
1177 if provfn in self.dataCaches[mc].packages[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001178 rprovides.add(package)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001179 for package in self.dataCaches[mc].packages_dynamic:
1180 if provfn in self.dataCaches[mc].packages_dynamic[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001181 rprovides.add(package)
1182 if not commonprovs:
1183 commonprovs = set(provides)
1184 else:
1185 commonprovs &= provides
1186 provide_results[provfn] = provides
1187 if not commonrprovs:
1188 commonrprovs = set(rprovides)
1189 else:
1190 commonrprovs &= rprovides
1191 rprovide_results[provfn] = rprovides
Andrew Geissler595f6302022-01-24 19:11:47 +00001192 #msgs.append("\nCommon provides:\n %s" % ("\n ".join(commonprovs)))
1193 #msgs.append("\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001194 for provfn in prov_list[prov]:
Andrew Geissler595f6302022-01-24 19:11:47 +00001195 msgs.append("\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs)))
1196 msgs.append("\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001197
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001198 if self.warn_multi_bb:
Andrew Geissler595f6302022-01-24 19:11:47 +00001199 logger.verbnote("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001200 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00001201 logger.error("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001202
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001203 self.init_progress_reporter.next_stage()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001204 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001205 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001206
1207 # Iterate over the task list looking for tasks with a 'setscene' function
Andrew Geissler82c905d2020-04-13 13:39:40 -05001208 self.runq_setscene_tids = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001209 if not self.cooker.configuration.nosetscene:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001210 for tid in self.runtaskentries:
1211 (mc, fn, taskname, _) = split_tid_mcfn(tid)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001212 setscenetid = tid + "_setscene"
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001213 if setscenetid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001214 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -05001215 self.runq_setscene_tids.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001216
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001217 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001218 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001219
1220 # Invalidate task if force mode active
1221 if self.cooker.configuration.force:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001222 for tid in self.target_tids:
1223 invalidate_task(tid, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001224
1225 # Invalidate task if invalidate mode active
1226 if self.cooker.configuration.invalidate_stamp:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001227 for tid in self.target_tids:
1228 fn = fn_from_tid(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001229 for st in self.cooker.configuration.invalidate_stamp.split(','):
1230 if not st.startswith("do_"):
1231 st = "do_%s" % st
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001232 invalidate_task(fn + ":" + st, True)
1233
1234 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001235 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001236
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001237 # Create and print to the logs a virtual/xxxx -> PN (fn) table
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001238 for mc in taskData:
1239 virtmap = taskData[mc].get_providermap(prefix="virtual/")
1240 virtpnmap = {}
1241 for v in virtmap:
1242 virtpnmap[v] = self.dataCaches[mc].pkg_fn[virtmap[v]]
1243 bb.debug(2, "%s resolved to: %s (%s)" % (v, virtpnmap[v], virtmap[v]))
1244 if hasattr(bb.parse.siggen, "tasks_resolved"):
1245 bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
1246
1247 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001248 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001249
Brad Bishop00e122a2019-10-05 11:10:57 -04001250 bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
1251
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001252 # Iterate over the task list and call into the siggen code
1253 dealtwith = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001254 todeal = set(self.runtaskentries)
Andrew Geissler595f6302022-01-24 19:11:47 +00001255 while todeal:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001256 for tid in todeal.copy():
Andrew Geissler595f6302022-01-24 19:11:47 +00001257 if not (self.runtaskentries[tid].depends - dealtwith):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001258 dealtwith.add(tid)
1259 todeal.remove(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001260 self.prepare_task_hash(tid)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001261 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001262
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001263 bb.parse.siggen.writeout_file_checksum_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001264
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001265 #self.dump_data()
1266 return len(self.runtaskentries)
1267
Brad Bishop19323692019-04-05 15:28:33 -04001268 def prepare_task_hash(self, tid):
Andrew Geissler517393d2023-01-13 08:55:19 -06001269 bb.parse.siggen.prep_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
1270 self.runtaskentries[tid].hash = bb.parse.siggen.get_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
Brad Bishop08902b02019-08-20 09:16:51 -04001271 self.runtaskentries[tid].unihash = bb.parse.siggen.get_unihash(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001272
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001273 def dump_data(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001274 """
1275 Dump some debug information on the internal data structures
1276 """
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001277 logger.debug3("run_tasks:")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001278 for tid in self.runtaskentries:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001279 logger.debug3(" %s: %s Deps %s RevDeps %s", tid,
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001280 self.runtaskentries[tid].weight,
1281 self.runtaskentries[tid].depends,
1282 self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001283
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001284class RunQueueWorker():
1285 def __init__(self, process, pipe):
1286 self.process = process
1287 self.pipe = pipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001288
1289class RunQueue:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001290 def __init__(self, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001291
1292 self.cooker = cooker
1293 self.cfgData = cfgData
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001294 self.rqdata = RunQueueData(self, cooker, cfgData, dataCaches, taskData, targets)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001295
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001296 self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION") or None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001297 self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID") or None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001298
1299 self.state = runQueuePrepare
1300
1301 # For disk space monitor
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001302 # Invoked at regular time intervals via the bitbake heartbeat event
1303 # while the build is running. We generate a unique name for the handler
1304 # here, just in case that there ever is more than one RunQueue instance,
Brad Bishop96ff1982019-08-19 13:50:42 -04001305 # start the handler when reaching runQueueSceneInit, and stop it when
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001306 # done with the build.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001307 self.dm = monitordisk.diskMonitor(cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001308 self.dm_event_handler_name = '_bb_diskmonitor_' + str(id(self))
1309 self.dm_event_handler_registered = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001310 self.rqexe = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001311 self.worker = {}
1312 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001313
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001314 def _start_worker(self, mc, fakeroot = False, rqexec = None):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001315 logger.debug("Starting bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001316 magic = "decafbad"
1317 if self.cooker.configuration.profile:
1318 magic = "decafbadbad"
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001319 fakerootlogs = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001320 if fakeroot:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001321 magic = magic + "beef"
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001322 mcdata = self.cooker.databuilder.mcdata[mc]
Brad Bishop19323692019-04-05 15:28:33 -04001323 fakerootcmd = shlex.split(mcdata.getVar("FAKEROOTCMD"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001324 fakerootenv = (mcdata.getVar("FAKEROOTBASEENV") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001325 env = os.environ.copy()
1326 for key, value in (var.split('=') for var in fakerootenv):
1327 env[key] = value
Brad Bishop19323692019-04-05 15:28:33 -04001328 worker = subprocess.Popen(fakerootcmd + ["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001329 fakerootlogs = self.rqdata.dataCaches[mc].fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001330 else:
1331 worker = subprocess.Popen(["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
1332 bb.utils.nonblockingfd(worker.stdout)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001333 workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, self, rqexec, fakerootlogs=fakerootlogs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334
1335 workerdata = {
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001336 "sigdata" : bb.parse.siggen.get_taskdata(),
Andrew Geissler82c905d2020-04-13 13:39:40 -05001337 "logdefaultlevel" : bb.msg.loggerDefaultLogLevel,
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001338 "build_verbose_shell" : self.cooker.configuration.build_verbose_shell,
1339 "build_verbose_stdout" : self.cooker.configuration.build_verbose_stdout,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001340 "logdefaultdomain" : bb.msg.loggerDefaultDomains,
1341 "prhost" : self.cooker.prhost,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001342 "buildname" : self.cfgData.getVar("BUILDNAME"),
1343 "date" : self.cfgData.getVar("DATE"),
1344 "time" : self.cfgData.getVar("TIME"),
Brad Bishopa34c0302019-09-23 22:34:48 -04001345 "hashservaddr" : self.cooker.hashservaddr,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001346 "umask" : self.cfgData.getVar("BB_DEFAULT_UMASK"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001347 }
1348
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001349 worker.stdin.write(b"<cookerconfig>" + pickle.dumps(self.cooker.configuration) + b"</cookerconfig>")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001350 worker.stdin.write(b"<extraconfigdata>" + pickle.dumps(self.cooker.extraconfigdata) + b"</extraconfigdata>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001351 worker.stdin.write(b"<workerdata>" + pickle.dumps(workerdata) + b"</workerdata>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001352 worker.stdin.flush()
1353
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001354 return RunQueueWorker(worker, workerpipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001355
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001356 def _teardown_worker(self, worker):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001357 if not worker:
1358 return
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001359 logger.debug("Teardown for bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001360 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001361 worker.process.stdin.write(b"<quit></quit>")
1362 worker.process.stdin.flush()
1363 worker.process.stdin.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001364 except IOError:
1365 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001366 while worker.process.returncode is None:
1367 worker.pipe.read()
1368 worker.process.poll()
1369 while worker.pipe.read():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001370 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001371 worker.pipe.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001372
1373 def start_worker(self):
1374 if self.worker:
1375 self.teardown_workers()
1376 self.teardown = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001377 for mc in self.rqdata.dataCaches:
1378 self.worker[mc] = self._start_worker(mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001379
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001380 def start_fakeworker(self, rqexec, mc):
1381 if not mc in self.fakeworker:
1382 self.fakeworker[mc] = self._start_worker(mc, True, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001383
1384 def teardown_workers(self):
1385 self.teardown = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001386 for mc in self.worker:
1387 self._teardown_worker(self.worker[mc])
1388 self.worker = {}
1389 for mc in self.fakeworker:
1390 self._teardown_worker(self.fakeworker[mc])
1391 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001392
1393 def read_workers(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001394 for mc in self.worker:
1395 self.worker[mc].pipe.read()
1396 for mc in self.fakeworker:
1397 self.fakeworker[mc].pipe.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001398
1399 def active_fds(self):
1400 fds = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001401 for mc in self.worker:
1402 fds.append(self.worker[mc].pipe.input)
1403 for mc in self.fakeworker:
1404 fds.append(self.fakeworker[mc].pipe.input)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001405 return fds
1406
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001407 def check_stamp_task(self, tid, taskname = None, recurse = False, cache = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001408 def get_timestamp(f):
1409 try:
1410 if not os.access(f, os.F_OK):
1411 return None
1412 return os.stat(f)[stat.ST_MTIME]
1413 except:
1414 return None
1415
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001416 (mc, fn, tn, taskfn) = split_tid_mcfn(tid)
1417 if taskname is None:
1418 taskname = tn
1419
Andrew Geissler517393d2023-01-13 08:55:19 -06001420 stampfile = bb.parse.siggen.stampfile_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001421
1422 # If the stamp is missing, it's not current
1423 if not os.access(stampfile, os.F_OK):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001424 logger.debug2("Stampfile %s not available", stampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001425 return False
1426 # If it's a 'nostamp' task, it's not current
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001427 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001428 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001429 logger.debug2("%s.%s is nostamp\n", fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001430 return False
1431
Andrew Geissler517393d2023-01-13 08:55:19 -06001432 if taskname.endswith("_setscene"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001433 return True
1434
1435 if cache is None:
1436 cache = {}
1437
1438 iscurrent = True
1439 t1 = get_timestamp(stampfile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001440 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001441 if iscurrent:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001442 (mc2, fn2, taskname2, taskfn2) = split_tid_mcfn(dep)
Andrew Geissler517393d2023-01-13 08:55:19 -06001443 stampfile2 = bb.parse.siggen.stampfile_mcfn(taskname2, taskfn2)
1444 stampfile3 = bb.parse.siggen.stampfile_mcfn(taskname2 + "_setscene", taskfn2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001445 t2 = get_timestamp(stampfile2)
1446 t3 = get_timestamp(stampfile3)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001447 if t3 and not t2:
1448 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001449 if t3 and t3 > t2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001450 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00001451 if fn == fn2:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001452 if not t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001453 logger.debug2('Stampfile %s does not exist', stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001454 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001455 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001456 if t1 < t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001457 logger.debug2('Stampfile %s < %s', stampfile, stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001458 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001459 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001460 if recurse and iscurrent:
1461 if dep in cache:
1462 iscurrent = cache[dep]
1463 if not iscurrent:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001464 logger.debug2('Stampfile for dependency %s:%s invalid (cached)' % (fn2, taskname2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001465 else:
1466 iscurrent = self.check_stamp_task(dep, recurse=True, cache=cache)
1467 cache[dep] = iscurrent
1468 if recurse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001469 cache[tid] = iscurrent
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001470 return iscurrent
1471
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001472 def validate_hashes(self, tocheck, data, currentcount=0, siginfo=False, summary=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04001473 valid = set()
1474 if self.hashvalidate:
Brad Bishop08902b02019-08-20 09:16:51 -04001475 sq_data = {}
1476 sq_data['hash'] = {}
1477 sq_data['hashfn'] = {}
1478 sq_data['unihash'] = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04001479 for tid in tocheck:
1480 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04001481 sq_data['hash'][tid] = self.rqdata.runtaskentries[tid].hash
1482 sq_data['hashfn'][tid] = self.rqdata.dataCaches[mc].hashfn[taskfn]
1483 sq_data['unihash'][tid] = self.rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -04001484
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001485 valid = self.validate_hash(sq_data, data, siginfo, currentcount, summary)
Brad Bishop96ff1982019-08-19 13:50:42 -04001486
1487 return valid
1488
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001489 def validate_hash(self, sq_data, d, siginfo, currentcount, summary):
1490 locs = {"sq_data" : sq_data, "d" : d, "siginfo" : siginfo, "currentcount" : currentcount, "summary" : summary}
Brad Bishop19323692019-04-05 15:28:33 -04001491
Brad Bishop08902b02019-08-20 09:16:51 -04001492 # Metadata has **kwargs so args can be added, sq_data can also gain new fields
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001493 call = self.hashvalidate + "(sq_data, d, siginfo=siginfo, currentcount=currentcount, summary=summary)"
Brad Bishop19323692019-04-05 15:28:33 -04001494
Brad Bishop19323692019-04-05 15:28:33 -04001495 return bb.utils.better_eval(call, locs)
1496
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001497 def _execute_runqueue(self):
1498 """
1499 Run the tasks in a queue prepared by rqdata.prepare()
1500 Upon failure, optionally try to recover the build using any alternate providers
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001501 (if the halt on failure configuration option isn't set)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001502 """
1503
1504 retval = True
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001505 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001506
1507 if self.state is runQueuePrepare:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001508 # NOTE: if you add, remove or significantly refactor the stages of this
1509 # process then you should recalculate the weightings here. This is quite
1510 # easy to do - just change the next line temporarily to pass debug=True as
1511 # the last parameter and you'll get a printout of the weightings as well
1512 # as a map to the lines where next_stage() was called. Of course this isn't
1513 # critical, but it helps to keep the progress reporting accurate.
1514 self.rqdata.init_progress_reporter = bb.progress.MultiStageProcessProgressReporter(self.cooker.data,
1515 "Initialising tasks",
1516 [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 -05001517 if self.rqdata.prepare() == 0:
1518 self.state = runQueueComplete
1519 else:
1520 self.state = runQueueSceneInit
Brad Bishop00e122a2019-10-05 11:10:57 -04001521 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001522
1523 if self.state is runQueueSceneInit:
Brad Bishop96ff1982019-08-19 13:50:42 -04001524 self.rqdata.init_progress_reporter.next_stage()
1525
1526 # we are ready to run, emit dependency info to any UI or class which
1527 # needs it
1528 depgraph = self.cooker.buildDependTree(self, self.rqdata.taskData)
1529 self.rqdata.init_progress_reporter.next_stage()
1530 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
1531
Brad Bishope2d5b612018-11-23 10:55:50 +13001532 if not self.dm_event_handler_registered:
1533 res = bb.event.register(self.dm_event_handler_name,
Andrew Geissler517393d2023-01-13 08:55:19 -06001534 lambda x, y: self.dm.check(self) if self.state in [runQueueRunning, runQueueCleanUp] else False,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001535 ('bb.event.HeartbeatEvent',), data=self.cfgData)
Brad Bishope2d5b612018-11-23 10:55:50 +13001536 self.dm_event_handler_registered = True
1537
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001538 dump = self.cooker.configuration.dump_signatures
1539 if dump:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001540 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001541 if 'printdiff' in dump:
1542 invalidtasks = self.print_diffscenetasks()
1543 self.dump_signatures(dump)
1544 if 'printdiff' in dump:
1545 self.write_diffscenetasks(invalidtasks)
1546 self.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001547
Brad Bishop96ff1982019-08-19 13:50:42 -04001548 if self.state is runQueueSceneInit:
1549 self.rqdata.init_progress_reporter.next_stage()
1550 self.start_worker()
1551 self.rqdata.init_progress_reporter.next_stage()
1552 self.rqexe = RunQueueExecute(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001553
Brad Bishop96ff1982019-08-19 13:50:42 -04001554 # If we don't have any setscene functions, skip execution
Andrew Geissler595f6302022-01-24 19:11:47 +00001555 if not self.rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04001556 logger.info('No setscene tasks')
1557 for tid in self.rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00001558 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04001559 self.rqexe.setbuildable(tid)
1560 self.rqexe.tasks_notcovered.add(tid)
1561 self.rqexe.sqdone = True
1562 logger.info('Executing Tasks')
1563 self.state = runQueueRunning
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001564
1565 if self.state is runQueueRunning:
1566 retval = self.rqexe.execute()
1567
1568 if self.state is runQueueCleanUp:
1569 retval = self.rqexe.finish()
1570
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001571 build_done = self.state is runQueueComplete or self.state is runQueueFailed
1572
1573 if build_done and self.dm_event_handler_registered:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001574 bb.event.remove(self.dm_event_handler_name, None, data=self.cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001575 self.dm_event_handler_registered = False
1576
1577 if build_done and self.rqexe:
Brad Bishop08902b02019-08-20 09:16:51 -04001578 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001579 self.teardown_workers()
Brad Bishop96ff1982019-08-19 13:50:42 -04001580 if self.rqexe:
1581 if self.rqexe.stats.failed:
1582 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)
1583 else:
1584 # Let's avoid the word "failed" if nothing actually did
1585 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 -05001586
1587 if self.state is runQueueFailed:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001588 raise bb.runqueue.TaskFailure(self.rqexe.failed_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001589
1590 if self.state is runQueueComplete:
1591 # All done
1592 return False
1593
1594 # Loop
1595 return retval
1596
1597 def execute_runqueue(self):
1598 # Catch unexpected exceptions and ensure we exit when an error occurs, not loop.
1599 try:
1600 return self._execute_runqueue()
1601 except bb.runqueue.TaskFailure:
1602 raise
1603 except SystemExit:
1604 raise
1605 except bb.BBHandledException:
1606 try:
1607 self.teardown_workers()
1608 except:
1609 pass
1610 self.state = runQueueComplete
1611 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001612 except Exception as err:
1613 logger.exception("An uncaught exception occurred in runqueue")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001614 try:
1615 self.teardown_workers()
1616 except:
1617 pass
1618 self.state = runQueueComplete
1619 raise
1620
1621 def finish_runqueue(self, now = False):
1622 if not self.rqexe:
1623 self.state = runQueueComplete
1624 return
1625
1626 if now:
1627 self.rqexe.finish_now()
1628 else:
1629 self.rqexe.finish()
1630
Andrew Geissler517393d2023-01-13 08:55:19 -06001631 def _rq_dump_sigtid(self, tids):
1632 for tid in tids:
1633 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1634 dataCaches = self.rqdata.dataCaches
1635 bb.parse.siggen.dump_sigtask(taskfn, taskname, dataCaches[mc].stamp[taskfn], True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001636
1637 def dump_signatures(self, options):
Andrew Geissler517393d2023-01-13 08:55:19 -06001638 if bb.cooker.CookerFeatures.RECIPE_SIGGEN_INFO not in self.cooker.featureset:
1639 bb.fatal("The dump signatures functionality needs the RECIPE_SIGGEN_INFO feature enabled")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001640
Andrew Geissler517393d2023-01-13 08:55:19 -06001641 bb.note("Writing task signature files")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001642
1643 max_process = int(self.cfgData.getVar("BB_NUMBER_PARSE_THREADS") or os.cpu_count() or 1)
Andrew Geissler517393d2023-01-13 08:55:19 -06001644 def chunkify(l, n):
1645 return [l[i::n] for i in range(n)]
1646 tids = chunkify(list(self.rqdata.runtaskentries), max_process)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001647 # We cannot use the real multiprocessing.Pool easily due to some local data
1648 # that can't be pickled. This is a cheap multi-process solution.
1649 launched = []
Andrew Geissler517393d2023-01-13 08:55:19 -06001650 while tids:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001651 if len(launched) < max_process:
Andrew Geissler517393d2023-01-13 08:55:19 -06001652 p = Process(target=self._rq_dump_sigtid, args=(tids.pop(), ))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001653 p.start()
1654 launched.append(p)
1655 for q in launched:
1656 # The finished processes are joined when calling is_alive()
1657 if not q.is_alive():
1658 launched.remove(q)
1659 for p in launched:
1660 p.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001661
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001662 bb.parse.siggen.dump_sigs(self.rqdata.dataCaches, options)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001663
1664 return
1665
1666 def print_diffscenetasks(self):
1667
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001668 noexec = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001669 tocheck = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001670
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001671 for tid in self.rqdata.runtaskentries:
1672 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1673 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001674
1675 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001676 noexec.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001677 continue
1678
Brad Bishop96ff1982019-08-19 13:50:42 -04001679 tocheck.add(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001680
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001681 valid_new = self.validate_hashes(tocheck, self.cooker.data, 0, True, summary=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001682
1683 # Tasks which are both setscene and noexec never care about dependencies
1684 # We therefore find tasks which are setscene and noexec and mark their
1685 # unique dependencies as valid.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001686 for tid in noexec:
1687 if tid not in self.rqdata.runq_setscene_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001688 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001689 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001690 hasnoexecparents = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001691 for dep2 in self.rqdata.runtaskentries[dep].revdeps:
1692 if dep2 in self.rqdata.runq_setscene_tids and dep2 in noexec:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001693 continue
1694 hasnoexecparents = False
1695 break
1696 if hasnoexecparents:
1697 valid_new.add(dep)
1698
1699 invalidtasks = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001700 for tid in self.rqdata.runtaskentries:
1701 if tid not in valid_new and tid not in noexec:
1702 invalidtasks.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001703
1704 found = set()
1705 processed = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001706 for tid in invalidtasks:
1707 toprocess = set([tid])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001708 while toprocess:
1709 next = set()
1710 for t in toprocess:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001711 for dep in self.rqdata.runtaskentries[t].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001712 if dep in invalidtasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001713 found.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001714 if dep not in processed:
1715 processed.add(dep)
1716 next.add(dep)
1717 toprocess = next
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001718 if tid in found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001719 toprocess = set()
1720
1721 tasklist = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001722 for tid in invalidtasks.difference(found):
1723 tasklist.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001724
1725 if tasklist:
1726 bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
1727
1728 return invalidtasks.difference(found)
1729
1730 def write_diffscenetasks(self, invalidtasks):
1731
1732 # Define recursion callback
1733 def recursecb(key, hash1, hash2):
1734 hashes = [hash1, hash2]
1735 hashfiles = bb.siggen.find_siginfo(key, None, hashes, self.cfgData)
1736
1737 recout = []
1738 if len(hashfiles) == 2:
1739 out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
Brad Bishopc342db32019-05-15 21:57:59 -04001740 recout.extend(list(' ' + l for l in out2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001741 else:
1742 recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
1743
1744 return recout
1745
1746
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001747 for tid in invalidtasks:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001748 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1749 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001750 h = self.rqdata.runtaskentries[tid].hash
Patrick Williams03907ee2022-05-01 06:28:52 -05001751 matches = bb.siggen.find_siginfo(pn, taskname, [], self.cooker.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001752 match = None
1753 for m in matches:
1754 if h in m:
1755 match = m
1756 if match is None:
1757 bb.fatal("Can't find a task we're supposed to have written out? (hash: %s)?" % h)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001758 matches = {k : v for k, v in iter(matches.items()) if h not in k}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001759 if matches:
1760 latestmatch = sorted(matches.keys(), key=lambda f: matches[f])[-1]
Brad Bishop19323692019-04-05 15:28:33 -04001761 prevh = __find_sha256__.search(latestmatch).group(0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001762 output = bb.siggen.compare_sigfiles(latestmatch, match, recursecb)
1763 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))
1764
Brad Bishop96ff1982019-08-19 13:50:42 -04001765
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001766class RunQueueExecute:
1767
1768 def __init__(self, rq):
1769 self.rq = rq
1770 self.cooker = rq.cooker
1771 self.cfgData = rq.cfgData
1772 self.rqdata = rq.rqdata
1773
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001774 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
1775 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001776 self.max_cpu_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_CPU")
1777 self.max_io_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_IO")
Patrick Williams92b42cb2022-09-03 06:53:57 -05001778 self.max_memory_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_MEMORY")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001779
Brad Bishop96ff1982019-08-19 13:50:42 -04001780 self.sq_buildable = set()
1781 self.sq_running = set()
1782 self.sq_live = set()
1783
Brad Bishop08902b02019-08-20 09:16:51 -04001784 self.updated_taskhash_queue = []
1785 self.pending_migrations = set()
1786
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001787 self.runq_buildable = set()
1788 self.runq_running = set()
1789 self.runq_complete = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001790 self.runq_tasksrun = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001791
1792 self.build_stamps = {}
1793 self.build_stamps2 = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001794 self.failed_tids = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001795 self.sq_deferred = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001796
1797 self.stampcache = {}
1798
Brad Bishop08902b02019-08-20 09:16:51 -04001799 self.holdoff_tasks = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04001800 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04001801 self.sqdone = False
1802
Andrew Geissler5199d832021-09-24 16:47:35 -05001803 self.stats = RunQueueStats(len(self.rqdata.runtaskentries), len(self.rqdata.runq_setscene_tids))
Brad Bishop96ff1982019-08-19 13:50:42 -04001804
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001805 for mc in rq.worker:
1806 rq.worker[mc].pipe.setrunqueueexec(self)
1807 for mc in rq.fakeworker:
1808 rq.fakeworker[mc].pipe.setrunqueueexec(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001809
1810 if self.number_tasks <= 0:
1811 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks)
1812
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001813 lower_limit = 1.0
1814 upper_limit = 1000000.0
1815 if self.max_cpu_pressure:
1816 self.max_cpu_pressure = float(self.max_cpu_pressure)
1817 if self.max_cpu_pressure < lower_limit:
1818 bb.fatal("Invalid BB_PRESSURE_MAX_CPU %s, minimum value is %s." % (self.max_cpu_pressure, lower_limit))
1819 if self.max_cpu_pressure > upper_limit:
1820 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))
1821
1822 if self.max_io_pressure:
1823 self.max_io_pressure = float(self.max_io_pressure)
1824 if self.max_io_pressure < lower_limit:
1825 bb.fatal("Invalid BB_PRESSURE_MAX_IO %s, minimum value is %s." % (self.max_io_pressure, lower_limit))
1826 if self.max_io_pressure > upper_limit:
1827 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))
1828
Patrick Williams92b42cb2022-09-03 06:53:57 -05001829 if self.max_memory_pressure:
1830 self.max_memory_pressure = float(self.max_memory_pressure)
1831 if self.max_memory_pressure < lower_limit:
1832 bb.fatal("Invalid BB_PRESSURE_MAX_MEMORY %s, minimum value is %s." % (self.max_memory_pressure, lower_limit))
1833 if self.max_memory_pressure > upper_limit:
1834 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))
1835
Brad Bishop96ff1982019-08-19 13:50:42 -04001836 # List of setscene tasks which we've covered
1837 self.scenequeue_covered = set()
1838 # List of tasks which are covered (including setscene ones)
1839 self.tasks_covered = set()
1840 self.tasks_scenequeue_done = set()
1841 self.scenequeue_notcovered = set()
1842 self.tasks_notcovered = set()
1843 self.scenequeue_notneeded = set()
1844
Brad Bishop08902b02019-08-20 09:16:51 -04001845 # We can't skip specified target tasks which aren't setscene tasks
1846 self.cantskip = set(self.rqdata.target_tids)
1847 self.cantskip.difference_update(self.rqdata.runq_setscene_tids)
1848 self.cantskip.intersection_update(self.rqdata.runtaskentries)
Brad Bishop96ff1982019-08-19 13:50:42 -04001849
1850 schedulers = self.get_schedulers()
1851 for scheduler in schedulers:
1852 if self.scheduler == scheduler.name:
1853 self.sched = scheduler(self, self.rqdata)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001854 logger.debug("Using runqueue scheduler '%s'", scheduler.name)
Brad Bishop96ff1982019-08-19 13:50:42 -04001855 break
1856 else:
1857 bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
1858 (self.scheduler, ", ".join(obj.name for obj in schedulers)))
1859
Andrew Geissler595f6302022-01-24 19:11:47 +00001860 #if self.rqdata.runq_setscene_tids:
Brad Bishop08902b02019-08-20 09:16:51 -04001861 self.sqdata = SQData()
1862 build_scenequeue_data(self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self)
Brad Bishop96ff1982019-08-19 13:50:42 -04001863
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001864 def runqueue_process_waitpid(self, task, status, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001865
1866 # self.build_stamps[pid] may not exist when use shared work directory.
1867 if task in self.build_stamps:
1868 self.build_stamps2.remove(self.build_stamps[task])
1869 del self.build_stamps[task]
1870
Brad Bishop96ff1982019-08-19 13:50:42 -04001871 if task in self.sq_live:
1872 if status != 0:
1873 self.sq_task_fail(task, status)
1874 else:
1875 self.sq_task_complete(task)
1876 self.sq_live.remove(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05001877 self.stats.updateActiveSetscene(len(self.sq_live))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001878 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04001879 if status != 0:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001880 self.task_fail(task, status, fakerootlog=fakerootlog)
Brad Bishop96ff1982019-08-19 13:50:42 -04001881 else:
1882 self.task_complete(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001883 return True
1884
1885 def finish_now(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001886 for mc in self.rq.worker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001887 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001888 self.rq.worker[mc].process.stdin.write(b"<finishnow></finishnow>")
1889 self.rq.worker[mc].process.stdin.flush()
1890 except IOError:
1891 # worker must have died?
1892 pass
1893 for mc in self.rq.fakeworker:
1894 try:
1895 self.rq.fakeworker[mc].process.stdin.write(b"<finishnow></finishnow>")
1896 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001897 except IOError:
1898 # worker must have died?
1899 pass
1900
Andrew Geissler595f6302022-01-24 19:11:47 +00001901 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001902 self.rq.state = runQueueFailed
1903 return
1904
1905 self.rq.state = runQueueComplete
1906 return
1907
1908 def finish(self):
1909 self.rq.state = runQueueCleanUp
1910
Andrew Geissler5199d832021-09-24 16:47:35 -05001911 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001912 if active > 0:
1913 bb.event.fire(runQueueExitWait(active), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001914 self.rq.read_workers()
1915 return self.rq.active_fds()
1916
Andrew Geissler595f6302022-01-24 19:11:47 +00001917 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001918 self.rq.state = runQueueFailed
1919 return True
1920
1921 self.rq.state = runQueueComplete
1922 return True
1923
Brad Bishop96ff1982019-08-19 13:50:42 -04001924 # Used by setscene only
1925 def check_dependencies(self, task, taskdeps):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001926 if not self.rq.depvalidate:
1927 return False
1928
Brad Bishop08902b02019-08-20 09:16:51 -04001929 # Must not edit parent data
1930 taskdeps = set(taskdeps)
1931
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001932 taskdata = {}
1933 taskdeps.add(task)
1934 for dep in taskdeps:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001935 (mc, fn, taskname, taskfn) = split_tid_mcfn(dep)
1936 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001937 taskdata[dep] = [pn, taskname, fn]
1938 call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001939 locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001940 valid = bb.utils.better_eval(call, locs)
1941 return valid
1942
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001943 def can_start_task(self):
Andrew Geissler5199d832021-09-24 16:47:35 -05001944 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001945 can_start = active < self.number_tasks
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001946 return can_start
1947
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001948 def get_schedulers(self):
1949 schedulers = set(obj for obj in globals().values()
1950 if type(obj) is type and
1951 issubclass(obj, RunQueueScheduler))
1952
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001953 user_schedulers = self.cfgData.getVar("BB_SCHEDULERS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001954 if user_schedulers:
1955 for sched in user_schedulers.split():
1956 if not "." in sched:
1957 bb.note("Ignoring scheduler '%s' from BB_SCHEDULERS: not an import" % sched)
1958 continue
1959
1960 modname, name = sched.rsplit(".", 1)
1961 try:
1962 module = __import__(modname, fromlist=(name,))
1963 except ImportError as exc:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001964 bb.fatal("Unable to import scheduler '%s' from '%s': %s" % (name, modname, exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001965 else:
1966 schedulers.add(getattr(module, name))
1967 return schedulers
1968
1969 def setbuildable(self, task):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001970 self.runq_buildable.add(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001971 self.sched.newbuildable(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001972
1973 def task_completeoutright(self, task):
1974 """
1975 Mark a task as completed
1976 Look at the reverse dependencies and mark any task with
1977 completed dependencies as buildable
1978 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001979 self.runq_complete.add(task)
1980 for revdep in self.rqdata.runtaskentries[task].revdeps:
1981 if revdep in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001982 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001983 if revdep in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001984 continue
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001985 alldeps = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001986 for dep in self.rqdata.runtaskentries[revdep].depends:
1987 if dep not in self.runq_complete:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001988 alldeps = False
1989 break
1990 if alldeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001991 self.setbuildable(revdep)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001992 logger.debug("Marking task %s as buildable", revdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001993
Andrew Geissler5199d832021-09-24 16:47:35 -05001994 for t in self.sq_deferred.copy():
1995 if self.sq_deferred[t] == task:
1996 logger.debug2("Deferred task %s now buildable" % t)
1997 del self.sq_deferred[t]
1998 update_scenequeue_data([t], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
1999
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002000 def task_complete(self, task):
2001 self.stats.taskCompleted()
2002 bb.event.fire(runQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
2003 self.task_completeoutright(task)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002004 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002005
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002006 def task_fail(self, task, exitcode, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002007 """
2008 Called when a task has failed
2009 Updates the state engine with the failure
2010 """
2011 self.stats.taskFailed()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002012 self.failed_tids.append(task)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002013
Andrew Geissler595f6302022-01-24 19:11:47 +00002014 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002015 if fakerootlog and os.path.exists(fakerootlog):
2016 with open(fakerootlog) as fakeroot_log_file:
2017 fakeroot_failed = False
2018 for line in reversed(fakeroot_log_file.readlines()):
2019 for fakeroot_error in ['mismatch', 'error', 'fatal']:
2020 if fakeroot_error in line.lower():
2021 fakeroot_failed = True
2022 if 'doing new pid setup and server start' in line:
2023 break
Andrew Geissler595f6302022-01-24 19:11:47 +00002024 fakeroot_log.append(line)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002025
2026 if not fakeroot_failed:
Andrew Geissler595f6302022-01-24 19:11:47 +00002027 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002028
Andrew Geissler595f6302022-01-24 19:11:47 +00002029 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 -05002030
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002031 if self.rqdata.taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002032 self.rq.state = runQueueCleanUp
2033
2034 def task_skip(self, task, reason):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002035 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002036 self.setbuildable(task)
2037 bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
2038 self.task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002039 self.stats.taskSkipped()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002040 self.stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002041
Brad Bishop08902b02019-08-20 09:16:51 -04002042 def summarise_scenequeue_errors(self):
2043 err = False
2044 if not self.sqdone:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002045 logger.debug('We could skip tasks %s', "\n".join(sorted(self.scenequeue_covered)))
Andrew Geissler5199d832021-09-24 16:47:35 -05002046 completeevent = sceneQueueComplete(self.stats, self.rq)
Brad Bishop08902b02019-08-20 09:16:51 -04002047 bb.event.fire(completeevent, self.cfgData)
2048 if self.sq_deferred:
2049 logger.error("Scenequeue had deferred entries: %s" % pprint.pformat(self.sq_deferred))
2050 err = True
2051 if self.updated_taskhash_queue:
2052 logger.error("Scenequeue had unprocessed changed taskhash entries: %s" % pprint.pformat(self.updated_taskhash_queue))
2053 err = True
2054 if self.holdoff_tasks:
2055 logger.error("Scenequeue had holdoff tasks: %s" % pprint.pformat(self.holdoff_tasks))
2056 err = True
2057
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002058 for tid in self.scenequeue_covered.intersection(self.scenequeue_notcovered):
2059 # No task should end up in both covered and uncovered, that is a bug.
2060 logger.error("Setscene task %s in both covered and notcovered." % tid)
2061
Brad Bishop08902b02019-08-20 09:16:51 -04002062 for tid in self.rqdata.runq_setscene_tids:
2063 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2064 err = True
2065 logger.error("Setscene Task %s was never marked as covered or not covered" % tid)
2066 if tid not in self.sq_buildable:
2067 err = True
2068 logger.error("Setscene Task %s was never marked as buildable" % tid)
2069 if tid not in self.sq_running:
2070 err = True
2071 logger.error("Setscene Task %s was never marked as running" % tid)
2072
2073 for x in self.rqdata.runtaskentries:
2074 if x not in self.tasks_covered and x not in self.tasks_notcovered:
2075 logger.error("Task %s was never moved from the setscene queue" % x)
2076 err = True
2077 if x not in self.tasks_scenequeue_done:
2078 logger.error("Task %s was never processed by the setscene code" % x)
2079 err = True
Andrew Geissler595f6302022-01-24 19:11:47 +00002080 if not self.rqdata.runtaskentries[x].depends and x not in self.runq_buildable:
Brad Bishop08902b02019-08-20 09:16:51 -04002081 logger.error("Task %s was never marked as buildable by the setscene code" % x)
2082 err = True
2083 return err
2084
2085
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002086 def execute(self):
2087 """
Brad Bishop96ff1982019-08-19 13:50:42 -04002088 Run the tasks in a queue prepared by prepare_runqueue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002089 """
2090
2091 self.rq.read_workers()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002092 if self.updated_taskhash_queue or self.pending_migrations:
2093 self.process_possible_migrations()
2094
2095 if not hasattr(self, "sorted_setscene_tids"):
2096 # Don't want to sort this set every execution
2097 self.sorted_setscene_tids = sorted(self.rqdata.runq_setscene_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002098
Brad Bishop96ff1982019-08-19 13:50:42 -04002099 task = None
2100 if not self.sqdone and self.can_start_task():
2101 # Find the next setscene to run
Andrew Geissler82c905d2020-04-13 13:39:40 -05002102 for nexttask in self.sorted_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002103 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 +00002104 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 -04002105 if nexttask not in self.rqdata.target_tids:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002106 logger.debug2("Skipping setscene for task %s" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002107 self.sq_task_skip(nexttask)
2108 self.scenequeue_notneeded.add(nexttask)
2109 if nexttask in self.sq_deferred:
2110 del self.sq_deferred[nexttask]
2111 return True
Brad Bishop08902b02019-08-20 09:16:51 -04002112 # If covered tasks are running, need to wait for them to complete
2113 for t in self.sqdata.sq_covered_tasks[nexttask]:
2114 if t in self.runq_running and t not in self.runq_complete:
2115 continue
Brad Bishop96ff1982019-08-19 13:50:42 -04002116 if nexttask in self.sq_deferred:
2117 if self.sq_deferred[nexttask] not in self.runq_complete:
2118 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002119 logger.debug("Task %s no longer deferred" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002120 del self.sq_deferred[nexttask]
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002121 valid = self.rq.validate_hashes(set([nexttask]), self.cooker.data, 0, False, summary=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002122 if not valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002123 logger.debug("%s didn't become valid, skipping setscene" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002124 self.sq_task_failoutright(nexttask)
2125 return True
Brad Bishop96ff1982019-08-19 13:50:42 -04002126 if nexttask in self.sqdata.outrightfail:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002127 logger.debug2('No package found, so skipping setscene task %s', nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002128 self.sq_task_failoutright(nexttask)
2129 return True
2130 if nexttask in self.sqdata.unskippable:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002131 logger.debug2("Setscene task %s is unskippable" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002132 task = nexttask
2133 break
2134 if task is not None:
2135 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
2136 taskname = taskname + "_setscene"
2137 if self.rq.check_stamp_task(task, taskname_from_tid(task), recurse = True, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002138 logger.debug2('Stamp for underlying task %s is current, so skipping setscene variant', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002139 self.sq_task_failoutright(task)
2140 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002141
Brad Bishop96ff1982019-08-19 13:50:42 -04002142 if self.cooker.configuration.force:
2143 if task in self.rqdata.target_tids:
2144 self.sq_task_failoutright(task)
2145 return True
2146
2147 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002148 logger.debug2('Setscene stamp current task %s, so skip it and its dependencies', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002149 self.sq_task_skip(task)
2150 return True
2151
2152 if self.cooker.configuration.skipsetscene:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002153 logger.debug2('No setscene tasks should be executed. Skipping %s', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002154 self.sq_task_failoutright(task)
2155 return True
2156
Andrew Geissler5199d832021-09-24 16:47:35 -05002157 startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
Brad Bishop96ff1982019-08-19 13:50:42 -04002158 bb.event.fire(startevent, self.cfgData)
2159
Brad Bishop96ff1982019-08-19 13:50:42 -04002160 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Andrew Geissler517393d2023-01-13 08:55:19 -06002161 runtask = {
2162 'fn' : taskfn,
2163 'task' : task,
2164 'taskname' : taskname,
2165 'taskhash' : self.rqdata.get_task_hash(task),
2166 'unihash' : self.rqdata.get_task_unihash(task),
2167 'quieterrors' : True,
2168 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
2169 'taskdepdata' : self.sq_build_taskdepdata(task),
2170 'dry_run' : False,
2171 'taskdep': taskdep,
2172 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2173 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2174 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2175 }
2176
Brad Bishop96ff1982019-08-19 13:50:42 -04002177 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
2178 if not mc in self.rq.fakeworker:
2179 self.rq.start_fakeworker(self, mc)
Andrew Geissler517393d2023-01-13 08:55:19 -06002180 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002181 self.rq.fakeworker[mc].process.stdin.flush()
2182 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06002183 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002184 self.rq.worker[mc].process.stdin.flush()
2185
Andrew Geissler517393d2023-01-13 08:55:19 -06002186 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002187 self.build_stamps2.append(self.build_stamps[task])
2188 self.sq_running.add(task)
2189 self.sq_live.add(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05002190 self.stats.updateActiveSetscene(len(self.sq_live))
Brad Bishop96ff1982019-08-19 13:50:42 -04002191 if self.can_start_task():
2192 return True
2193
Brad Bishopc68388fc2019-08-26 01:33:31 -04002194 self.update_holdofftasks()
2195
Brad Bishop08902b02019-08-20 09:16:51 -04002196 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 -05002197 hashequiv_logger.verbose("Setscene tasks completed")
Brad Bishop96ff1982019-08-19 13:50:42 -04002198
Brad Bishop08902b02019-08-20 09:16:51 -04002199 err = self.summarise_scenequeue_errors()
Brad Bishop96ff1982019-08-19 13:50:42 -04002200 if err:
2201 self.rq.state = runQueueFailed
2202 return True
2203
2204 if self.cooker.configuration.setsceneonly:
2205 self.rq.state = runQueueComplete
2206 return True
2207 self.sqdone = True
2208
2209 if self.stats.total == 0:
2210 # nothing to do
2211 self.rq.state = runQueueComplete
2212 return True
2213
2214 if self.cooker.configuration.setsceneonly:
2215 task = None
2216 else:
2217 task = self.sched.next()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002218 if task is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002219 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002220
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002221 if self.rqdata.setscene_ignore_tasks is not None:
2222 if self.check_setscene_ignore_tasks(task):
2223 self.task_fail(task, "setscene ignore_tasks")
Brad Bishop96ff1982019-08-19 13:50:42 -04002224 return True
2225
2226 if task in self.tasks_covered:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002227 logger.debug2("Setscene covered task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002228 self.task_skip(task, "covered")
2229 return True
2230
2231 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002232 logger.debug2("Stamp current task %s", task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002233
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002234 self.task_skip(task, "existing")
Andrew Geissler82c905d2020-04-13 13:39:40 -05002235 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002236 return True
2237
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002238 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002239 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2240 startevent = runQueueTaskStarted(task, self.stats, self.rq,
2241 noexec=True)
2242 bb.event.fire(startevent, self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002243 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002244 self.stats.taskActive()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002245 if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Andrew Geissler517393d2023-01-13 08:55:19 -06002246 bb.build.make_stamp_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002247 self.task_complete(task)
2248 return True
2249 else:
2250 startevent = runQueueTaskStarted(task, self.stats, self.rq)
2251 bb.event.fire(startevent, self.cfgData)
2252
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002253 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Andrew Geissler517393d2023-01-13 08:55:19 -06002254 runtask = {
2255 'fn' : taskfn,
2256 'task' : task,
2257 'taskname' : taskname,
2258 'taskhash' : self.rqdata.get_task_hash(task),
2259 'unihash' : self.rqdata.get_task_unihash(task),
2260 'quieterrors' : False,
2261 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
2262 'taskdepdata' : self.build_taskdepdata(task),
2263 'dry_run' : self.rqdata.setscene_enforce,
2264 'taskdep': taskdep,
2265 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2266 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2267 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2268 }
2269
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002270 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 -05002271 if not mc in self.rq.fakeworker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002272 try:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002273 self.rq.start_fakeworker(self, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002274 except OSError as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002275 logger.critical("Failed to spawn fakeroot worker to run %s: %s" % (task, str(exc)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002276 self.rq.state = runQueueFailed
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002277 self.stats.taskFailed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002278 return True
Andrew Geissler517393d2023-01-13 08:55:19 -06002279 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002280 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002281 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06002282 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002283 self.rq.worker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002284
Andrew Geissler517393d2023-01-13 08:55:19 -06002285 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002286 self.build_stamps2.append(self.build_stamps[task])
2287 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002288 self.stats.taskActive()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002289 if self.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002290 return True
2291
Andrew Geissler595f6302022-01-24 19:11:47 +00002292 if self.stats.active > 0 or self.sq_live:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002293 self.rq.read_workers()
2294 return self.rq.active_fds()
2295
Brad Bishop96ff1982019-08-19 13:50:42 -04002296 # No more tasks can be run. If we have deferred setscene tasks we should run them.
2297 if self.sq_deferred:
Patrick Williams92b42cb2022-09-03 06:53:57 -05002298 deferred_tid = list(self.sq_deferred.keys())[0]
2299 blocking_tid = self.sq_deferred.pop(deferred_tid)
Patrick Williams2390b1b2022-11-03 13:47:49 -05002300 logger.warning("Runqueue deadlocked on deferred tasks, forcing task %s blocked by %s" % (deferred_tid, blocking_tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04002301 return True
2302
Andrew Geissler595f6302022-01-24 19:11:47 +00002303 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002304 self.rq.state = runQueueFailed
2305 return True
2306
2307 # Sanity Checks
Brad Bishop08902b02019-08-20 09:16:51 -04002308 err = self.summarise_scenequeue_errors()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002309 for task in self.rqdata.runtaskentries:
2310 if task not in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002311 logger.error("Task %s never buildable!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002312 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002313 elif task not in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002314 logger.error("Task %s never ran!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002315 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002316 elif task not in self.runq_complete:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002317 logger.error("Task %s never completed!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002318 err = True
2319
2320 if err:
2321 self.rq.state = runQueueFailed
2322 else:
2323 self.rq.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002324
2325 return True
2326
Brad Bishopc68388fc2019-08-26 01:33:31 -04002327 def filtermcdeps(self, task, mc, deps):
Andrew Geissler99467da2019-02-25 18:54:23 -06002328 ret = set()
Andrew Geissler99467da2019-02-25 18:54:23 -06002329 for dep in deps:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002330 thismc = mc_from_tid(dep)
2331 if thismc != mc:
Andrew Geissler99467da2019-02-25 18:54:23 -06002332 continue
2333 ret.add(dep)
2334 return ret
2335
Brad Bishopa34c0302019-09-23 22:34:48 -04002336 # We filter out multiconfig dependencies from taskdepdata we pass to the tasks
Andrew Geissler99467da2019-02-25 18:54:23 -06002337 # as most code can't handle them
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002338 def build_taskdepdata(self, task):
2339 taskdepdata = {}
Brad Bishopc68388fc2019-08-26 01:33:31 -04002340 mc = mc_from_tid(task)
Brad Bishop08902b02019-08-20 09:16:51 -04002341 next = self.rqdata.runtaskentries[task].depends.copy()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002342 next.add(task)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002343 next = self.filtermcdeps(task, mc, next)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002344 while next:
2345 additional = []
2346 for revdep in next:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002347 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2348 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2349 deps = self.rqdata.runtaskentries[revdep].depends
2350 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002351 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002352 unihash = self.rqdata.runtaskentries[revdep].unihash
Brad Bishopc68388fc2019-08-26 01:33:31 -04002353 deps = self.filtermcdeps(task, mc, deps)
Brad Bishop19323692019-04-05 15:28:33 -04002354 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002355 for revdep2 in deps:
2356 if revdep2 not in taskdepdata:
2357 additional.append(revdep2)
2358 next = additional
2359
2360 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2361 return taskdepdata
2362
Brad Bishop08902b02019-08-20 09:16:51 -04002363 def update_holdofftasks(self):
Brad Bishopc68388fc2019-08-26 01:33:31 -04002364
2365 if not self.holdoff_need_update:
2366 return
2367
2368 notcovered = set(self.scenequeue_notcovered)
2369 notcovered |= self.cantskip
2370 for tid in self.scenequeue_notcovered:
2371 notcovered |= self.sqdata.sq_covered_tasks[tid]
2372 notcovered |= self.sqdata.unskippable.difference(self.rqdata.runq_setscene_tids)
2373 notcovered.intersection_update(self.tasks_scenequeue_done)
2374
2375 covered = set(self.scenequeue_covered)
2376 for tid in self.scenequeue_covered:
2377 covered |= self.sqdata.sq_covered_tasks[tid]
2378 covered.difference_update(notcovered)
2379 covered.intersection_update(self.tasks_scenequeue_done)
2380
2381 for tid in notcovered | covered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002382 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002383 self.setbuildable(tid)
2384 elif self.rqdata.runtaskentries[tid].depends.issubset(self.runq_complete):
2385 self.setbuildable(tid)
2386
2387 self.tasks_covered = covered
2388 self.tasks_notcovered = notcovered
2389
Brad Bishop08902b02019-08-20 09:16:51 -04002390 self.holdoff_tasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002391
Brad Bishop08902b02019-08-20 09:16:51 -04002392 for tid in self.rqdata.runq_setscene_tids:
2393 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2394 self.holdoff_tasks.add(tid)
2395
2396 for tid in self.holdoff_tasks.copy():
2397 for dep in self.sqdata.sq_covered_tasks[tid]:
2398 if dep not in self.runq_complete:
2399 self.holdoff_tasks.add(dep)
2400
Brad Bishopc68388fc2019-08-26 01:33:31 -04002401 self.holdoff_need_update = False
2402
Brad Bishop08902b02019-08-20 09:16:51 -04002403 def process_possible_migrations(self):
2404
2405 changed = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002406 toprocess = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002407 for tid, unihash in self.updated_taskhash_queue.copy():
2408 if tid in self.runq_running and tid not in self.runq_complete:
2409 continue
2410
2411 self.updated_taskhash_queue.remove((tid, unihash))
2412
2413 if unihash != self.rqdata.runtaskentries[tid].unihash:
Andrew Geisslerc926e172021-05-07 16:11:35 -05002414 # Make sure we rehash any other tasks with the same task hash that we're deferred against.
2415 torehash = [tid]
2416 for deftid in self.sq_deferred:
2417 if self.sq_deferred[deftid] == tid:
2418 torehash.append(deftid)
2419 for hashtid in torehash:
2420 hashequiv_logger.verbose("Task %s unihash changed to %s" % (hashtid, unihash))
2421 self.rqdata.runtaskentries[hashtid].unihash = unihash
2422 bb.parse.siggen.set_unihash(hashtid, unihash)
2423 toprocess.add(hashtid)
Andrew Geissler78b72792022-06-14 06:47:25 -05002424 if torehash:
2425 # Need to save after set_unihash above
2426 bb.parse.siggen.save_unitaskhashes()
Brad Bishop08902b02019-08-20 09:16:51 -04002427
Andrew Geissler82c905d2020-04-13 13:39:40 -05002428 # Work out all tasks which depend upon these
2429 total = set()
2430 next = set()
2431 for p in toprocess:
2432 next |= self.rqdata.runtaskentries[p].revdeps
2433 while next:
2434 current = next.copy()
2435 total = total | next
2436 next = set()
2437 for ntid in current:
2438 next |= self.rqdata.runtaskentries[ntid].revdeps
2439 next.difference_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002440
Andrew Geissler82c905d2020-04-13 13:39:40 -05002441 # Now iterate those tasks in dependency order to regenerate their taskhash/unihash
2442 next = set()
2443 for p in total:
Andrew Geissler595f6302022-01-24 19:11:47 +00002444 if not self.rqdata.runtaskentries[p].depends:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002445 next.add(p)
2446 elif self.rqdata.runtaskentries[p].depends.isdisjoint(total):
2447 next.add(p)
2448
2449 # When an item doesn't have dependencies in total, we can process it. Drop items from total when handled
2450 while next:
2451 current = next.copy()
2452 next = set()
2453 for tid in current:
Andrew Geissler595f6302022-01-24 19:11:47 +00002454 if self.rqdata.runtaskentries[p].depends and not self.rqdata.runtaskentries[tid].depends.isdisjoint(total):
Andrew Geissler82c905d2020-04-13 13:39:40 -05002455 continue
2456 orighash = self.rqdata.runtaskentries[tid].hash
Andrew Geissler517393d2023-01-13 08:55:19 -06002457 newhash = bb.parse.siggen.get_taskhash(tid, self.rqdata.runtaskentries[tid].depends, self.rqdata.dataCaches)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002458 origuni = self.rqdata.runtaskentries[tid].unihash
2459 newuni = bb.parse.siggen.get_unihash(tid)
2460 # FIXME, need to check it can come from sstate at all for determinism?
2461 remapped = False
2462 if newuni == origuni:
2463 # Nothing to do, we match, skip code below
2464 remapped = True
2465 elif tid in self.scenequeue_covered or tid in self.sq_live:
2466 # Already ran this setscene task or it running. Report the new taskhash
2467 bb.parse.siggen.report_unihash_equiv(tid, newhash, origuni, newuni, self.rqdata.dataCaches)
2468 hashequiv_logger.verbose("Already covered setscene for %s so ignoring rehash (remap)" % (tid))
2469 remapped = True
2470
2471 if not remapped:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002472 #logger.debug("Task %s hash changes: %s->%s %s->%s" % (tid, orighash, newhash, origuni, newuni))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002473 self.rqdata.runtaskentries[tid].hash = newhash
2474 self.rqdata.runtaskentries[tid].unihash = newuni
2475 changed.add(tid)
2476
2477 next |= self.rqdata.runtaskentries[tid].revdeps
2478 total.remove(tid)
2479 next.intersection_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002480
2481 if changed:
2482 for mc in self.rq.worker:
2483 self.rq.worker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2484 for mc in self.rq.fakeworker:
2485 self.rq.fakeworker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2486
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002487 hashequiv_logger.debug(pprint.pformat("Tasks changed:\n%s" % (changed)))
Brad Bishop08902b02019-08-20 09:16:51 -04002488
2489 for tid in changed:
2490 if tid not in self.rqdata.runq_setscene_tids:
2491 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002492 if tid not in self.pending_migrations:
2493 self.pending_migrations.add(tid)
2494
Andrew Geissler82c905d2020-04-13 13:39:40 -05002495 update_tasks = []
Brad Bishop08902b02019-08-20 09:16:51 -04002496 for tid in self.pending_migrations.copy():
Andrew Geissler82c905d2020-04-13 13:39:40 -05002497 if tid in self.runq_running or tid in self.sq_live:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002498 # Too late, task already running, not much we can do now
2499 self.pending_migrations.remove(tid)
2500 continue
2501
Brad Bishop08902b02019-08-20 09:16:51 -04002502 valid = True
2503 # Check no tasks this covers are running
2504 for dep in self.sqdata.sq_covered_tasks[tid]:
2505 if dep in self.runq_running and dep not in self.runq_complete:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002506 hashequiv_logger.debug2("Task %s is running which blocks setscene for %s from running" % (dep, tid))
Brad Bishop08902b02019-08-20 09:16:51 -04002507 valid = False
2508 break
2509 if not valid:
2510 continue
2511
2512 self.pending_migrations.remove(tid)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002513 changed = True
Brad Bishop08902b02019-08-20 09:16:51 -04002514
2515 if tid in self.tasks_scenequeue_done:
2516 self.tasks_scenequeue_done.remove(tid)
2517 for dep in self.sqdata.sq_covered_tasks[tid]:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002518 if dep in self.runq_complete and dep not in self.runq_tasksrun:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002519 bb.error("Task %s marked as completed but now needing to rerun? Halting build." % dep)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002520 self.failed_tids.append(tid)
2521 self.rq.state = runQueueCleanUp
2522 return
2523
Brad Bishop08902b02019-08-20 09:16:51 -04002524 if dep not in self.runq_complete:
2525 if dep in self.tasks_scenequeue_done and dep not in self.sqdata.unskippable:
2526 self.tasks_scenequeue_done.remove(dep)
2527
2528 if tid in self.sq_buildable:
2529 self.sq_buildable.remove(tid)
2530 if tid in self.sq_running:
2531 self.sq_running.remove(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002532 if tid in self.sqdata.outrightfail:
2533 self.sqdata.outrightfail.remove(tid)
2534 if tid in self.scenequeue_notcovered:
2535 self.scenequeue_notcovered.remove(tid)
2536 if tid in self.scenequeue_covered:
2537 self.scenequeue_covered.remove(tid)
2538 if tid in self.scenequeue_notneeded:
2539 self.scenequeue_notneeded.remove(tid)
2540
2541 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2542 self.sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2543
2544 if tid in self.stampcache:
2545 del self.stampcache[tid]
2546
2547 if tid in self.build_stamps:
2548 del self.build_stamps[tid]
2549
2550 update_tasks.append(tid)
2551
2552 update_tasks2 = []
2553 for tid in update_tasks:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002554 harddepfail = False
2555 for t in self.sqdata.sq_harddeps:
2556 if tid in self.sqdata.sq_harddeps[t] and t in self.scenequeue_notcovered:
2557 harddepfail = True
2558 break
2559 if not harddepfail and self.sqdata.sq_revdeps[tid].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
Brad Bishop08902b02019-08-20 09:16:51 -04002560 if tid not in self.sq_buildable:
2561 self.sq_buildable.add(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +00002562 if not self.sqdata.sq_revdeps[tid]:
Brad Bishop08902b02019-08-20 09:16:51 -04002563 self.sq_buildable.add(tid)
2564
Andrew Geissler517393d2023-01-13 08:55:19 -06002565 update_tasks2.append((tid, harddepfail, tid in self.sqdata.valid))
Brad Bishop08902b02019-08-20 09:16:51 -04002566
Andrew Geissler517393d2023-01-13 08:55:19 -06002567 if update_tasks2:
Brad Bishop08902b02019-08-20 09:16:51 -04002568 self.sqdone = False
Patrick Williams92b42cb2022-09-03 06:53:57 -05002569 for mc in sorted(self.sqdata.multiconfigs):
Andrew Geissler517393d2023-01-13 08:55:19 -06002570 for tid in sorted([t[0] for t in update_tasks2]):
Patrick Williams92b42cb2022-09-03 06:53:57 -05002571 if mc_from_tid(tid) != mc:
2572 continue
2573 h = pending_hash_index(tid, self.rqdata)
2574 if h in self.sqdata.hashes and tid != self.sqdata.hashes[h]:
2575 self.sq_deferred[tid] = self.sqdata.hashes[h]
2576 bb.note("Deferring %s after %s" % (tid, self.sqdata.hashes[h]))
Andrew Geissler517393d2023-01-13 08:55:19 -06002577 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 -05002578
Andrew Geissler517393d2023-01-13 08:55:19 -06002579 for (tid, harddepfail, origvalid) in update_tasks2:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002580 if tid in self.sqdata.valid and not origvalid:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002581 hashequiv_logger.verbose("Setscene task %s became valid" % tid)
2582 if harddepfail:
Andrew Geissler517393d2023-01-13 08:55:19 -06002583 logger.debug2("%s has an unavailable hard dependency so skipping" % (tid))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002584 self.sq_task_failoutright(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002585
2586 if changed:
Andrew Geissler5199d832021-09-24 16:47:35 -05002587 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002588 self.holdoff_need_update = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002589
Brad Bishop96ff1982019-08-19 13:50:42 -04002590 def scenequeue_updatecounters(self, task, fail=False):
Brad Bishop08902b02019-08-20 09:16:51 -04002591
2592 for dep in sorted(self.sqdata.sq_deps[task]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002593 if fail and task in self.sqdata.sq_harddeps and dep in self.sqdata.sq_harddeps[task]:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002594 if dep in self.scenequeue_covered or dep in self.scenequeue_notcovered:
2595 # dependency could be already processed, e.g. noexec setscene task
2596 continue
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002597 noexec, stamppresent = check_setscene_stamps(dep, self.rqdata, self.rq, self.stampcache)
2598 if noexec or stamppresent:
2599 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002600 logger.debug2("%s was unavailable and is a hard dependency of %s so skipping" % (task, dep))
Brad Bishop96ff1982019-08-19 13:50:42 -04002601 self.sq_task_failoutright(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002602 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002603 if self.sqdata.sq_revdeps[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2604 if dep not in self.sq_buildable:
2605 self.sq_buildable.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002606
Brad Bishop96ff1982019-08-19 13:50:42 -04002607 next = set([task])
2608 while next:
2609 new = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002610 for t in sorted(next):
Brad Bishop96ff1982019-08-19 13:50:42 -04002611 self.tasks_scenequeue_done.add(t)
2612 # Look down the dependency chain for non-setscene things which this task depends on
2613 # and mark as 'done'
2614 for dep in self.rqdata.runtaskentries[t].depends:
2615 if dep in self.rqdata.runq_setscene_tids or dep in self.tasks_scenequeue_done:
2616 continue
2617 if self.rqdata.runtaskentries[dep].revdeps.issubset(self.tasks_scenequeue_done):
2618 new.add(dep)
Brad Bishop96ff1982019-08-19 13:50:42 -04002619 next = new
2620
Andrew Geissler5199d832021-09-24 16:47:35 -05002621 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002622 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002623
2624 def sq_task_completeoutright(self, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002625 """
2626 Mark a task as completed
2627 Look at the reverse dependencies and mark any task with
2628 completed dependencies as buildable
2629 """
2630
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002631 logger.debug('Found task %s which could be accelerated', task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002632 self.scenequeue_covered.add(task)
2633 self.scenequeue_updatecounters(task)
2634
Brad Bishop96ff1982019-08-19 13:50:42 -04002635 def sq_check_taskfail(self, task):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002636 if self.rqdata.setscene_ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002637 realtask = task.split('_setscene')[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002638 (mc, fn, taskname, taskfn) = split_tid_mcfn(realtask)
2639 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002640 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002641 logger.error('Task %s.%s failed' % (pn, taskname + "_setscene"))
2642 self.rq.state = runQueueCleanUp
2643
Brad Bishop96ff1982019-08-19 13:50:42 -04002644 def sq_task_complete(self, task):
Andrew Geissler5199d832021-09-24 16:47:35 -05002645 bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
Brad Bishop96ff1982019-08-19 13:50:42 -04002646 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002647
Brad Bishop96ff1982019-08-19 13:50:42 -04002648 def sq_task_fail(self, task, result):
Andrew Geissler5199d832021-09-24 16:47:35 -05002649 bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002650 self.scenequeue_notcovered.add(task)
2651 self.scenequeue_updatecounters(task, True)
Brad Bishop96ff1982019-08-19 13:50:42 -04002652 self.sq_check_taskfail(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002653
Brad Bishop96ff1982019-08-19 13:50:42 -04002654 def sq_task_failoutright(self, task):
2655 self.sq_running.add(task)
2656 self.sq_buildable.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002657 self.scenequeue_notcovered.add(task)
2658 self.scenequeue_updatecounters(task, True)
2659
Brad Bishop96ff1982019-08-19 13:50:42 -04002660 def sq_task_skip(self, task):
2661 self.sq_running.add(task)
2662 self.sq_buildable.add(task)
2663 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002664
Brad Bishop96ff1982019-08-19 13:50:42 -04002665 def sq_build_taskdepdata(self, task):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002666 def getsetscenedeps(tid):
2667 deps = set()
2668 (mc, fn, taskname, _) = split_tid_mcfn(tid)
2669 realtid = tid + "_setscene"
2670 idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
2671 for (depname, idependtask) in idepends:
2672 if depname not in self.rqdata.taskData[mc].build_targets:
2673 continue
2674
2675 depfn = self.rqdata.taskData[mc].build_targets[depname][0]
2676 if depfn is None:
2677 continue
2678 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2679 deps.add(deptid)
2680 return deps
2681
2682 taskdepdata = {}
2683 next = getsetscenedeps(task)
2684 next.add(task)
2685 while next:
2686 additional = []
2687 for revdep in next:
2688 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2689 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2690 deps = getsetscenedeps(revdep)
2691 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2692 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002693 unihash = self.rqdata.runtaskentries[revdep].unihash
2694 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002695 for revdep2 in deps:
2696 if revdep2 not in taskdepdata:
2697 additional.append(revdep2)
2698 next = additional
2699
2700 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2701 return taskdepdata
2702
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002703 def check_setscene_ignore_tasks(self, tid):
2704 # Check task that is going to run against the ignore tasks list
Brad Bishop96ff1982019-08-19 13:50:42 -04002705 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2706 # Ignore covered tasks
2707 if tid in self.tasks_covered:
2708 return False
2709 # Ignore stamped tasks
2710 if self.rq.check_stamp_task(tid, taskname, cache=self.stampcache):
2711 return False
2712 # Ignore noexec tasks
2713 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2714 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2715 return False
2716
2717 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002718 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Brad Bishop96ff1982019-08-19 13:50:42 -04002719 if tid in self.rqdata.runq_setscene_tids:
Andrew Geissler595f6302022-01-24 19:11:47 +00002720 msg = ['Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)]
Brad Bishop96ff1982019-08-19 13:50:42 -04002721 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002722 msg = ['Task %s.%s attempted to execute unexpectedly' % (pn, taskname)]
Andrew Geissler82c905d2020-04-13 13:39:40 -05002723 for t in self.scenequeue_notcovered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002724 msg.append("\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash))
2725 msg.append('\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered))
2726 logger.error("".join(msg))
Brad Bishop96ff1982019-08-19 13:50:42 -04002727 return True
2728 return False
2729
2730class SQData(object):
2731 def __init__(self):
2732 # SceneQueue dependencies
2733 self.sq_deps = {}
2734 # SceneQueue reverse dependencies
2735 self.sq_revdeps = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002736 # Injected inter-setscene task dependencies
2737 self.sq_harddeps = {}
2738 # Cache of stamp files so duplicates can't run in parallel
2739 self.stamps = {}
2740 # Setscene tasks directly depended upon by the build
2741 self.unskippable = set()
2742 # List of setscene tasks which aren't present
2743 self.outrightfail = set()
2744 # A list of normal tasks a setscene task covers
2745 self.sq_covered_tasks = {}
2746
2747def build_scenequeue_data(sqdata, rqdata, rq, cooker, stampcache, sqrq):
2748
2749 sq_revdeps = {}
2750 sq_revdeps_squash = {}
2751 sq_collated_deps = {}
2752
2753 # We need to construct a dependency graph for the setscene functions. Intermediate
2754 # dependencies between the setscene tasks only complicate the code. This code
2755 # therefore aims to collapse the huge runqueue dependency tree into a smaller one
2756 # only containing the setscene functions.
2757
2758 rqdata.init_progress_reporter.next_stage()
2759
2760 # First process the chains up to the first setscene task.
2761 endpoints = {}
2762 for tid in rqdata.runtaskentries:
2763 sq_revdeps[tid] = copy.copy(rqdata.runtaskentries[tid].revdeps)
2764 sq_revdeps_squash[tid] = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00002765 if not sq_revdeps[tid] and tid not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002766 #bb.warn("Added endpoint %s" % (tid))
2767 endpoints[tid] = set()
2768
2769 rqdata.init_progress_reporter.next_stage()
2770
2771 # Secondly process the chains between setscene tasks.
2772 for tid in rqdata.runq_setscene_tids:
2773 sq_collated_deps[tid] = set()
2774 #bb.warn("Added endpoint 2 %s" % (tid))
2775 for dep in rqdata.runtaskentries[tid].depends:
2776 if tid in sq_revdeps[dep]:
2777 sq_revdeps[dep].remove(tid)
2778 if dep not in endpoints:
2779 endpoints[dep] = set()
2780 #bb.warn(" Added endpoint 3 %s" % (dep))
2781 endpoints[dep].add(tid)
2782
2783 rqdata.init_progress_reporter.next_stage()
2784
2785 def process_endpoints(endpoints):
2786 newendpoints = {}
2787 for point, task in endpoints.items():
2788 tasks = set()
2789 if task:
2790 tasks |= task
2791 if sq_revdeps_squash[point]:
2792 tasks |= sq_revdeps_squash[point]
2793 if point not in rqdata.runq_setscene_tids:
2794 for t in tasks:
2795 sq_collated_deps[t].add(point)
2796 sq_revdeps_squash[point] = set()
2797 if point in rqdata.runq_setscene_tids:
2798 sq_revdeps_squash[point] = tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002799 continue
2800 for dep in rqdata.runtaskentries[point].depends:
2801 if point in sq_revdeps[dep]:
2802 sq_revdeps[dep].remove(point)
2803 if tasks:
2804 sq_revdeps_squash[dep] |= tasks
Andrew Geissler595f6302022-01-24 19:11:47 +00002805 if not sq_revdeps[dep] and dep not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002806 newendpoints[dep] = task
Andrew Geissler595f6302022-01-24 19:11:47 +00002807 if newendpoints:
Brad Bishop96ff1982019-08-19 13:50:42 -04002808 process_endpoints(newendpoints)
2809
2810 process_endpoints(endpoints)
2811
2812 rqdata.init_progress_reporter.next_stage()
2813
Brad Bishop08902b02019-08-20 09:16:51 -04002814 # Build a list of tasks which are "unskippable"
2815 # These are direct endpoints referenced by the build upto and including setscene tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002816 # Take the build endpoints (no revdeps) and find the sstate tasks they depend upon
2817 new = True
2818 for tid in rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00002819 if not rqdata.runtaskentries[tid].revdeps:
Brad Bishop96ff1982019-08-19 13:50:42 -04002820 sqdata.unskippable.add(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002821 sqdata.unskippable |= sqrq.cantskip
Brad Bishop96ff1982019-08-19 13:50:42 -04002822 while new:
2823 new = False
Brad Bishop08902b02019-08-20 09:16:51 -04002824 orig = sqdata.unskippable.copy()
2825 for tid in sorted(orig, reverse=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04002826 if tid in rqdata.runq_setscene_tids:
2827 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00002828 if not rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04002829 # These are tasks which have no setscene tasks in their chain, need to mark as directly buildable
Brad Bishop96ff1982019-08-19 13:50:42 -04002830 sqrq.setbuildable(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002831 sqdata.unskippable |= rqdata.runtaskentries[tid].depends
Brad Bishop08902b02019-08-20 09:16:51 -04002832 if sqdata.unskippable != orig:
2833 new = True
2834
2835 sqrq.tasks_scenequeue_done |= sqdata.unskippable.difference(rqdata.runq_setscene_tids)
Brad Bishop96ff1982019-08-19 13:50:42 -04002836
2837 rqdata.init_progress_reporter.next_stage(len(rqdata.runtaskentries))
2838
2839 # Sanity check all dependencies could be changed to setscene task references
2840 for taskcounter, tid in enumerate(rqdata.runtaskentries):
2841 if tid in rqdata.runq_setscene_tids:
2842 pass
Andrew Geissler595f6302022-01-24 19:11:47 +00002843 elif sq_revdeps_squash[tid]:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002844 bb.msg.fatal("RunQueue", "Something went badly wrong during scenequeue generation, halting. Please report this problem.")
Brad Bishop96ff1982019-08-19 13:50:42 -04002845 else:
2846 del sq_revdeps_squash[tid]
2847 rqdata.init_progress_reporter.update(taskcounter)
2848
2849 rqdata.init_progress_reporter.next_stage()
2850
2851 # Resolve setscene inter-task dependencies
2852 # e.g. do_sometask_setscene[depends] = "targetname:do_someothertask_setscene"
2853 # Note that anything explicitly depended upon will have its reverse dependencies removed to avoid circular dependencies
2854 for tid in rqdata.runq_setscene_tids:
2855 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2856 realtid = tid + "_setscene"
2857 idepends = rqdata.taskData[mc].taskentries[realtid].idepends
Andrew Geissler517393d2023-01-13 08:55:19 -06002858 sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2859
Brad Bishop96ff1982019-08-19 13:50:42 -04002860 for (depname, idependtask) in idepends:
2861
2862 if depname not in rqdata.taskData[mc].build_targets:
2863 continue
2864
2865 depfn = rqdata.taskData[mc].build_targets[depname][0]
2866 if depfn is None:
2867 continue
2868 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2869 if deptid not in rqdata.runtaskentries:
2870 bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (realtid, depfn, idependtask))
2871
2872 if not deptid in sqdata.sq_harddeps:
2873 sqdata.sq_harddeps[deptid] = set()
2874 sqdata.sq_harddeps[deptid].add(tid)
2875
2876 sq_revdeps_squash[tid].add(deptid)
2877 # Have to zero this to avoid circular dependencies
2878 sq_revdeps_squash[deptid] = set()
2879
2880 rqdata.init_progress_reporter.next_stage()
2881
2882 for task in sqdata.sq_harddeps:
2883 for dep in sqdata.sq_harddeps[task]:
2884 sq_revdeps_squash[dep].add(task)
2885
2886 rqdata.init_progress_reporter.next_stage()
2887
2888 #for tid in sq_revdeps_squash:
2889 # data = ""
2890 # for dep in sq_revdeps_squash[tid]:
2891 # data = data + "\n %s" % dep
2892 # bb.warn("Task %s_setscene: is %s " % (tid, data))
2893
2894 sqdata.sq_revdeps = sq_revdeps_squash
Brad Bishop96ff1982019-08-19 13:50:42 -04002895 sqdata.sq_covered_tasks = sq_collated_deps
2896
2897 # Build reverse version of revdeps to populate deps structure
2898 for tid in sqdata.sq_revdeps:
2899 sqdata.sq_deps[tid] = set()
2900 for tid in sqdata.sq_revdeps:
2901 for dep in sqdata.sq_revdeps[tid]:
2902 sqdata.sq_deps[dep].add(tid)
2903
2904 rqdata.init_progress_reporter.next_stage()
2905
Brad Bishop00e122a2019-10-05 11:10:57 -04002906 sqdata.multiconfigs = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002907 for tid in sqdata.sq_revdeps:
Brad Bishop00e122a2019-10-05 11:10:57 -04002908 sqdata.multiconfigs.add(mc_from_tid(tid))
Andrew Geissler595f6302022-01-24 19:11:47 +00002909 if not sqdata.sq_revdeps[tid]:
Brad Bishop96ff1982019-08-19 13:50:42 -04002910 sqrq.sq_buildable.add(tid)
2911
2912 rqdata.init_progress_reporter.finish()
2913
Brad Bishop00e122a2019-10-05 11:10:57 -04002914 sqdata.noexec = set()
2915 sqdata.stamppresent = set()
2916 sqdata.valid = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002917
Patrick Williams213cb262021-08-07 19:21:33 -05002918 sqdata.hashes = {}
2919 sqrq.sq_deferred = {}
2920 for mc in sorted(sqdata.multiconfigs):
2921 for tid in sorted(sqdata.sq_revdeps):
2922 if mc_from_tid(tid) != mc:
2923 continue
2924 h = pending_hash_index(tid, rqdata)
2925 if h not in sqdata.hashes:
2926 sqdata.hashes[h] = tid
2927 else:
2928 sqrq.sq_deferred[tid] = sqdata.hashes[h]
2929 bb.note("Deferring %s after %s" % (tid, sqdata.hashes[h]))
2930
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002931 update_scenequeue_data(sqdata.sq_revdeps, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002932
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002933 # Compute a list of 'stale' sstate tasks where the current hash does not match the one
2934 # in any stamp files. Pass the list out to metadata as an event.
2935 found = {}
2936 for tid in rqdata.runq_setscene_tids:
2937 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002938 stamps = bb.build.find_stale_stamps(taskname, taskfn)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002939 if stamps:
2940 if mc not in found:
2941 found[mc] = {}
2942 found[mc][tid] = stamps
2943 for mc in found:
2944 event = bb.event.StaleSetSceneTasks(found[mc])
2945 bb.event.fire(event, cooker.databuilder.mcdata[mc])
2946
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002947def check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=False):
2948
2949 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2950
2951 taskdep = rqdata.dataCaches[mc].task_deps[taskfn]
2952
2953 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Andrew Geissler517393d2023-01-13 08:55:19 -06002954 bb.build.make_stamp_mcfn(taskname + "_setscene", taskfn)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002955 return True, False
2956
2957 if rq.check_stamp_task(tid, taskname + "_setscene", cache=stampcache):
2958 logger.debug2('Setscene stamp current for task %s', tid)
2959 return False, True
2960
2961 if rq.check_stamp_task(tid, taskname, recurse = True, cache=stampcache):
2962 logger.debug2('Normal stamp current for task %s', tid)
2963 return False, True
2964
2965 return False, False
2966
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002967def update_scenequeue_data(tids, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True):
Brad Bishop00e122a2019-10-05 11:10:57 -04002968
2969 tocheck = set()
2970
2971 for tid in sorted(tids):
2972 if tid in sqdata.stamppresent:
2973 sqdata.stamppresent.remove(tid)
2974 if tid in sqdata.valid:
2975 sqdata.valid.remove(tid)
Andrew Geisslerc926e172021-05-07 16:11:35 -05002976 if tid in sqdata.outrightfail:
2977 sqdata.outrightfail.remove(tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04002978
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002979 noexec, stamppresent = check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002980
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002981 if noexec:
Brad Bishop00e122a2019-10-05 11:10:57 -04002982 sqdata.noexec.add(tid)
2983 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002984 logger.debug2("%s is noexec so skipping setscene" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04002985 continue
2986
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002987 if stamppresent:
Brad Bishop00e122a2019-10-05 11:10:57 -04002988 sqdata.stamppresent.add(tid)
2989 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002990 logger.debug2("%s has a valid stamp, skipping" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04002991 continue
2992
2993 tocheck.add(tid)
2994
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002995 sqdata.valid |= rq.validate_hashes(tocheck, cooker.data, len(sqdata.stamppresent), False, summary=summary)
Brad Bishop00e122a2019-10-05 11:10:57 -04002996
Patrick Williams213cb262021-08-07 19:21:33 -05002997 for tid in tids:
2998 if tid in sqdata.stamppresent:
2999 continue
3000 if tid in sqdata.valid:
3001 continue
3002 if tid in sqdata.noexec:
3003 continue
3004 if tid in sqrq.scenequeue_covered:
3005 continue
3006 if tid in sqrq.scenequeue_notcovered:
3007 continue
3008 if tid in sqrq.sq_deferred:
3009 continue
3010 sqdata.outrightfail.add(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003011 logger.debug2("%s already handled (fallthrough), skipping" % (tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04003012
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003013class TaskFailure(Exception):
3014 """
3015 Exception raised when a task in a runqueue fails
3016 """
3017 def __init__(self, x):
3018 self.args = x
3019
3020
3021class runQueueExitWait(bb.event.Event):
3022 """
3023 Event when waiting for task processes to exit
3024 """
3025
3026 def __init__(self, remain):
3027 self.remain = remain
3028 self.message = "Waiting for %s active tasks to finish" % remain
3029 bb.event.Event.__init__(self)
3030
3031class runQueueEvent(bb.event.Event):
3032 """
3033 Base runQueue event class
3034 """
3035 def __init__(self, task, stats, rq):
3036 self.taskid = task
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003037 self.taskstring = task
3038 self.taskname = taskname_from_tid(task)
3039 self.taskfile = fn_from_tid(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003040 self.taskhash = rq.rqdata.get_task_hash(task)
3041 self.stats = stats.copy()
3042 bb.event.Event.__init__(self)
3043
3044class sceneQueueEvent(runQueueEvent):
3045 """
3046 Base sceneQueue event class
3047 """
3048 def __init__(self, task, stats, rq, noexec=False):
3049 runQueueEvent.__init__(self, task, stats, rq)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003050 self.taskstring = task + "_setscene"
3051 self.taskname = taskname_from_tid(task) + "_setscene"
3052 self.taskfile = fn_from_tid(task)
3053 self.taskhash = rq.rqdata.get_task_hash(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003054
3055class runQueueTaskStarted(runQueueEvent):
3056 """
3057 Event notifying a task was started
3058 """
3059 def __init__(self, task, stats, rq, noexec=False):
3060 runQueueEvent.__init__(self, task, stats, rq)
3061 self.noexec = noexec
3062
3063class sceneQueueTaskStarted(sceneQueueEvent):
3064 """
3065 Event notifying a setscene task was started
3066 """
3067 def __init__(self, task, stats, rq, noexec=False):
3068 sceneQueueEvent.__init__(self, task, stats, rq)
3069 self.noexec = noexec
3070
3071class runQueueTaskFailed(runQueueEvent):
3072 """
3073 Event notifying a task failed
3074 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003075 def __init__(self, task, stats, exitcode, rq, fakeroot_log=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003076 runQueueEvent.__init__(self, task, stats, rq)
3077 self.exitcode = exitcode
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003078 self.fakeroot_log = fakeroot_log
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003079
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003080 def __str__(self):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003081 if self.fakeroot_log:
3082 return "Task (%s) failed with exit code '%s' \nPseudo log:\n%s" % (self.taskstring, self.exitcode, self.fakeroot_log)
3083 else:
3084 return "Task (%s) failed with exit code '%s'" % (self.taskstring, self.exitcode)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003085
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003086class sceneQueueTaskFailed(sceneQueueEvent):
3087 """
3088 Event notifying a setscene task failed
3089 """
3090 def __init__(self, task, stats, exitcode, rq):
3091 sceneQueueEvent.__init__(self, task, stats, rq)
3092 self.exitcode = exitcode
3093
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003094 def __str__(self):
3095 return "Setscene task (%s) failed with exit code '%s' - real task will be run instead" % (self.taskstring, self.exitcode)
3096
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003097class sceneQueueComplete(sceneQueueEvent):
3098 """
3099 Event when all the sceneQueue tasks are complete
3100 """
3101 def __init__(self, stats, rq):
3102 self.stats = stats.copy()
3103 bb.event.Event.__init__(self)
3104
3105class runQueueTaskCompleted(runQueueEvent):
3106 """
3107 Event notifying a task completed
3108 """
3109
3110class sceneQueueTaskCompleted(sceneQueueEvent):
3111 """
3112 Event notifying a setscene task completed
3113 """
3114
3115class runQueueTaskSkipped(runQueueEvent):
3116 """
3117 Event notifying a task was skipped
3118 """
3119 def __init__(self, task, stats, rq, reason):
3120 runQueueEvent.__init__(self, task, stats, rq)
3121 self.reason = reason
3122
Brad Bishop08902b02019-08-20 09:16:51 -04003123class taskUniHashUpdate(bb.event.Event):
3124 """
3125 Base runQueue event class
3126 """
3127 def __init__(self, task, unihash):
3128 self.taskid = task
3129 self.unihash = unihash
3130 bb.event.Event.__init__(self)
3131
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003132class runQueuePipe():
3133 """
3134 Abstraction for a pipe between a worker thread and the server
3135 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003136 def __init__(self, pipein, pipeout, d, rq, rqexec, fakerootlogs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003137 self.input = pipein
3138 if pipeout:
3139 pipeout.close()
3140 bb.utils.nonblockingfd(self.input)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003141 self.queue = b""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003142 self.d = d
3143 self.rq = rq
3144 self.rqexec = rqexec
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003145 self.fakerootlogs = fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003146
3147 def setrunqueueexec(self, rqexec):
3148 self.rqexec = rqexec
3149
3150 def read(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003151 for workers, name in [(self.rq.worker, "Worker"), (self.rq.fakeworker, "Fakeroot")]:
3152 for worker in workers.values():
3153 worker.process.poll()
3154 if worker.process.returncode is not None and not self.rq.teardown:
3155 bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, worker.process.pid, str(worker.process.returncode)))
3156 self.rq.finish_runqueue(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003157
3158 start = len(self.queue)
3159 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003160 self.queue = self.queue + (self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003161 except (OSError, IOError) as e:
3162 if e.errno != errno.EAGAIN:
3163 raise
3164 end = len(self.queue)
3165 found = True
Andrew Geissler595f6302022-01-24 19:11:47 +00003166 while found and self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003167 found = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003168 index = self.queue.find(b"</event>")
3169 while index != -1 and self.queue.startswith(b"<event>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003170 try:
3171 event = pickle.loads(self.queue[7:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003172 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
3173 if isinstance(e, pickle.UnpicklingError) and "truncated" in str(e):
3174 # The pickled data could contain "</event>" so search for the next occurance
3175 # unpickling again, this should be the only way an unpickle error could occur
3176 index = self.queue.find(b"</event>", index + 1)
3177 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003178 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
3179 bb.event.fire_from_worker(event, self.d)
Brad Bishop08902b02019-08-20 09:16:51 -04003180 if isinstance(event, taskUniHashUpdate):
3181 self.rqexec.updated_taskhash_queue.append((event.taskid, event.unihash))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003182 found = True
3183 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003184 index = self.queue.find(b"</event>")
3185 index = self.queue.find(b"</exitcode>")
3186 while index != -1 and self.queue.startswith(b"<exitcode>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003187 try:
3188 task, status = pickle.loads(self.queue[10:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003189 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003190 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003191 (_, _, _, taskfn) = split_tid_mcfn(task)
3192 fakerootlog = None
3193 if self.fakerootlogs and taskfn and taskfn in self.fakerootlogs:
3194 fakerootlog = self.fakerootlogs[taskfn]
3195 self.rqexec.runqueue_process_waitpid(task, status, fakerootlog=fakerootlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003196 found = True
3197 self.queue = self.queue[index+11:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003198 index = self.queue.find(b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003199 return (end > start)
3200
3201 def close(self):
3202 while self.read():
3203 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00003204 if self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003205 print("Warning, worker left partial message: %s" % self.queue)
3206 self.input.close()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003207
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003208def get_setscene_enforce_ignore_tasks(d, targets):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003209 if d.getVar('BB_SETSCENE_ENFORCE') != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003210 return None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003211 ignore_tasks = (d.getVar("BB_SETSCENE_ENFORCE_IGNORE_TASKS") or "").split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003212 outlist = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003213 for item in ignore_tasks[:]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003214 if item.startswith('%:'):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003215 for (mc, target, task, fn) in targets:
3216 outlist.append(target + ':' + item.split(':')[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003217 else:
3218 outlist.append(item)
3219 return outlist
3220
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003221def check_setscene_enforce_ignore_tasks(pn, taskname, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003222 import fnmatch
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003223 if ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003224 item = '%s:%s' % (pn, taskname)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003225 for ignore_tasks in ignore_tasks:
3226 if fnmatch.fnmatch(item, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003227 return True
3228 return False
3229 return True