blob: 0bb3bc20ab3369a387860be813d4067ac13e7123 [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]
Patrick Williams520786c2023-06-25 16:20:36 -05002161 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002162 runtask = {
2163 'fn' : taskfn,
2164 'task' : task,
2165 'taskname' : taskname,
2166 'taskhash' : self.rqdata.get_task_hash(task),
2167 'unihash' : self.rqdata.get_task_unihash(task),
2168 'quieterrors' : True,
2169 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002170 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002171 'taskdepdata' : self.sq_build_taskdepdata(task),
2172 'dry_run' : False,
2173 'taskdep': taskdep,
2174 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2175 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2176 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2177 }
2178
Brad Bishop96ff1982019-08-19 13:50:42 -04002179 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
2180 if not mc in self.rq.fakeworker:
2181 self.rq.start_fakeworker(self, mc)
Andrew Geissler517393d2023-01-13 08:55:19 -06002182 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002183 self.rq.fakeworker[mc].process.stdin.flush()
2184 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06002185 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002186 self.rq.worker[mc].process.stdin.flush()
2187
Andrew Geissler517393d2023-01-13 08:55:19 -06002188 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002189 self.build_stamps2.append(self.build_stamps[task])
2190 self.sq_running.add(task)
2191 self.sq_live.add(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05002192 self.stats.updateActiveSetscene(len(self.sq_live))
Brad Bishop96ff1982019-08-19 13:50:42 -04002193 if self.can_start_task():
2194 return True
2195
Brad Bishopc68388fc2019-08-26 01:33:31 -04002196 self.update_holdofftasks()
2197
Brad Bishop08902b02019-08-20 09:16:51 -04002198 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 -05002199 hashequiv_logger.verbose("Setscene tasks completed")
Brad Bishop96ff1982019-08-19 13:50:42 -04002200
Brad Bishop08902b02019-08-20 09:16:51 -04002201 err = self.summarise_scenequeue_errors()
Brad Bishop96ff1982019-08-19 13:50:42 -04002202 if err:
2203 self.rq.state = runQueueFailed
2204 return True
2205
2206 if self.cooker.configuration.setsceneonly:
2207 self.rq.state = runQueueComplete
2208 return True
2209 self.sqdone = True
2210
2211 if self.stats.total == 0:
2212 # nothing to do
2213 self.rq.state = runQueueComplete
2214 return True
2215
2216 if self.cooker.configuration.setsceneonly:
2217 task = None
2218 else:
2219 task = self.sched.next()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002220 if task is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002221 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002222
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002223 if self.rqdata.setscene_ignore_tasks is not None:
2224 if self.check_setscene_ignore_tasks(task):
2225 self.task_fail(task, "setscene ignore_tasks")
Brad Bishop96ff1982019-08-19 13:50:42 -04002226 return True
2227
2228 if task in self.tasks_covered:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002229 logger.debug2("Setscene covered task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002230 self.task_skip(task, "covered")
2231 return True
2232
2233 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002234 logger.debug2("Stamp current task %s", task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002235
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002236 self.task_skip(task, "existing")
Andrew Geissler82c905d2020-04-13 13:39:40 -05002237 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002238 return True
2239
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002240 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002241 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2242 startevent = runQueueTaskStarted(task, self.stats, self.rq,
2243 noexec=True)
2244 bb.event.fire(startevent, self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002245 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002246 self.stats.taskActive()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002247 if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Andrew Geissler517393d2023-01-13 08:55:19 -06002248 bb.build.make_stamp_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002249 self.task_complete(task)
2250 return True
2251 else:
2252 startevent = runQueueTaskStarted(task, self.stats, self.rq)
2253 bb.event.fire(startevent, self.cfgData)
2254
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002255 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002256 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002257 runtask = {
2258 'fn' : taskfn,
2259 'task' : task,
2260 'taskname' : taskname,
2261 'taskhash' : self.rqdata.get_task_hash(task),
2262 'unihash' : self.rqdata.get_task_unihash(task),
2263 'quieterrors' : False,
2264 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002265 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002266 'taskdepdata' : self.build_taskdepdata(task),
2267 'dry_run' : self.rqdata.setscene_enforce,
2268 'taskdep': taskdep,
2269 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2270 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2271 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2272 }
2273
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002274 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 -05002275 if not mc in self.rq.fakeworker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002276 try:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002277 self.rq.start_fakeworker(self, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002278 except OSError as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002279 logger.critical("Failed to spawn fakeroot worker to run %s: %s" % (task, str(exc)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002280 self.rq.state = runQueueFailed
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002281 self.stats.taskFailed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002282 return True
Andrew Geissler517393d2023-01-13 08:55:19 -06002283 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002284 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002285 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06002286 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002287 self.rq.worker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002288
Andrew Geissler517393d2023-01-13 08:55:19 -06002289 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002290 self.build_stamps2.append(self.build_stamps[task])
2291 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002292 self.stats.taskActive()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002293 if self.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002294 return True
2295
Andrew Geissler595f6302022-01-24 19:11:47 +00002296 if self.stats.active > 0 or self.sq_live:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002297 self.rq.read_workers()
2298 return self.rq.active_fds()
2299
Brad Bishop96ff1982019-08-19 13:50:42 -04002300 # No more tasks can be run. If we have deferred setscene tasks we should run them.
2301 if self.sq_deferred:
Patrick Williams92b42cb2022-09-03 06:53:57 -05002302 deferred_tid = list(self.sq_deferred.keys())[0]
2303 blocking_tid = self.sq_deferred.pop(deferred_tid)
Patrick Williams2390b1b2022-11-03 13:47:49 -05002304 logger.warning("Runqueue deadlocked on deferred tasks, forcing task %s blocked by %s" % (deferred_tid, blocking_tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04002305 return True
2306
Andrew Geissler595f6302022-01-24 19:11:47 +00002307 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002308 self.rq.state = runQueueFailed
2309 return True
2310
2311 # Sanity Checks
Brad Bishop08902b02019-08-20 09:16:51 -04002312 err = self.summarise_scenequeue_errors()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002313 for task in self.rqdata.runtaskentries:
2314 if task not in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002315 logger.error("Task %s never buildable!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002316 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002317 elif task not in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002318 logger.error("Task %s never ran!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002319 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002320 elif task not in self.runq_complete:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002321 logger.error("Task %s never completed!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002322 err = True
2323
2324 if err:
2325 self.rq.state = runQueueFailed
2326 else:
2327 self.rq.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002328
2329 return True
2330
Brad Bishopc68388fc2019-08-26 01:33:31 -04002331 def filtermcdeps(self, task, mc, deps):
Andrew Geissler99467da2019-02-25 18:54:23 -06002332 ret = set()
Andrew Geissler99467da2019-02-25 18:54:23 -06002333 for dep in deps:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002334 thismc = mc_from_tid(dep)
2335 if thismc != mc:
Andrew Geissler99467da2019-02-25 18:54:23 -06002336 continue
2337 ret.add(dep)
2338 return ret
2339
Brad Bishopa34c0302019-09-23 22:34:48 -04002340 # We filter out multiconfig dependencies from taskdepdata we pass to the tasks
Andrew Geissler99467da2019-02-25 18:54:23 -06002341 # as most code can't handle them
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002342 def build_taskdepdata(self, task):
2343 taskdepdata = {}
Brad Bishopc68388fc2019-08-26 01:33:31 -04002344 mc = mc_from_tid(task)
Brad Bishop08902b02019-08-20 09:16:51 -04002345 next = self.rqdata.runtaskentries[task].depends.copy()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002346 next.add(task)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002347 next = self.filtermcdeps(task, mc, next)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002348 while next:
2349 additional = []
2350 for revdep in next:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002351 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2352 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2353 deps = self.rqdata.runtaskentries[revdep].depends
2354 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002355 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002356 unihash = self.rqdata.runtaskentries[revdep].unihash
Brad Bishopc68388fc2019-08-26 01:33:31 -04002357 deps = self.filtermcdeps(task, mc, deps)
Patrick Williamsb542dec2023-06-09 01:26:37 -05002358 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2359 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002360 for revdep2 in deps:
2361 if revdep2 not in taskdepdata:
2362 additional.append(revdep2)
2363 next = additional
2364
2365 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2366 return taskdepdata
2367
Brad Bishop08902b02019-08-20 09:16:51 -04002368 def update_holdofftasks(self):
Brad Bishopc68388fc2019-08-26 01:33:31 -04002369
2370 if not self.holdoff_need_update:
2371 return
2372
2373 notcovered = set(self.scenequeue_notcovered)
2374 notcovered |= self.cantskip
2375 for tid in self.scenequeue_notcovered:
2376 notcovered |= self.sqdata.sq_covered_tasks[tid]
2377 notcovered |= self.sqdata.unskippable.difference(self.rqdata.runq_setscene_tids)
2378 notcovered.intersection_update(self.tasks_scenequeue_done)
2379
2380 covered = set(self.scenequeue_covered)
2381 for tid in self.scenequeue_covered:
2382 covered |= self.sqdata.sq_covered_tasks[tid]
2383 covered.difference_update(notcovered)
2384 covered.intersection_update(self.tasks_scenequeue_done)
2385
2386 for tid in notcovered | covered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002387 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002388 self.setbuildable(tid)
2389 elif self.rqdata.runtaskentries[tid].depends.issubset(self.runq_complete):
2390 self.setbuildable(tid)
2391
2392 self.tasks_covered = covered
2393 self.tasks_notcovered = notcovered
2394
Brad Bishop08902b02019-08-20 09:16:51 -04002395 self.holdoff_tasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002396
Brad Bishop08902b02019-08-20 09:16:51 -04002397 for tid in self.rqdata.runq_setscene_tids:
2398 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2399 self.holdoff_tasks.add(tid)
2400
2401 for tid in self.holdoff_tasks.copy():
2402 for dep in self.sqdata.sq_covered_tasks[tid]:
2403 if dep not in self.runq_complete:
2404 self.holdoff_tasks.add(dep)
2405
Brad Bishopc68388fc2019-08-26 01:33:31 -04002406 self.holdoff_need_update = False
2407
Brad Bishop08902b02019-08-20 09:16:51 -04002408 def process_possible_migrations(self):
2409
2410 changed = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002411 toprocess = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002412 for tid, unihash in self.updated_taskhash_queue.copy():
2413 if tid in self.runq_running and tid not in self.runq_complete:
2414 continue
2415
2416 self.updated_taskhash_queue.remove((tid, unihash))
2417
2418 if unihash != self.rqdata.runtaskentries[tid].unihash:
Andrew Geisslerc926e172021-05-07 16:11:35 -05002419 # Make sure we rehash any other tasks with the same task hash that we're deferred against.
2420 torehash = [tid]
2421 for deftid in self.sq_deferred:
2422 if self.sq_deferred[deftid] == tid:
2423 torehash.append(deftid)
2424 for hashtid in torehash:
2425 hashequiv_logger.verbose("Task %s unihash changed to %s" % (hashtid, unihash))
2426 self.rqdata.runtaskentries[hashtid].unihash = unihash
2427 bb.parse.siggen.set_unihash(hashtid, unihash)
2428 toprocess.add(hashtid)
Andrew Geissler78b72792022-06-14 06:47:25 -05002429 if torehash:
2430 # Need to save after set_unihash above
2431 bb.parse.siggen.save_unitaskhashes()
Brad Bishop08902b02019-08-20 09:16:51 -04002432
Andrew Geissler82c905d2020-04-13 13:39:40 -05002433 # Work out all tasks which depend upon these
2434 total = set()
2435 next = set()
2436 for p in toprocess:
2437 next |= self.rqdata.runtaskentries[p].revdeps
2438 while next:
2439 current = next.copy()
2440 total = total | next
2441 next = set()
2442 for ntid in current:
2443 next |= self.rqdata.runtaskentries[ntid].revdeps
2444 next.difference_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002445
Andrew Geissler82c905d2020-04-13 13:39:40 -05002446 # Now iterate those tasks in dependency order to regenerate their taskhash/unihash
2447 next = set()
2448 for p in total:
Andrew Geissler595f6302022-01-24 19:11:47 +00002449 if not self.rqdata.runtaskentries[p].depends:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002450 next.add(p)
2451 elif self.rqdata.runtaskentries[p].depends.isdisjoint(total):
2452 next.add(p)
2453
2454 # When an item doesn't have dependencies in total, we can process it. Drop items from total when handled
2455 while next:
2456 current = next.copy()
2457 next = set()
2458 for tid in current:
Andrew Geissler595f6302022-01-24 19:11:47 +00002459 if self.rqdata.runtaskentries[p].depends and not self.rqdata.runtaskentries[tid].depends.isdisjoint(total):
Andrew Geissler82c905d2020-04-13 13:39:40 -05002460 continue
2461 orighash = self.rqdata.runtaskentries[tid].hash
Andrew Geissler517393d2023-01-13 08:55:19 -06002462 newhash = bb.parse.siggen.get_taskhash(tid, self.rqdata.runtaskentries[tid].depends, self.rqdata.dataCaches)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002463 origuni = self.rqdata.runtaskentries[tid].unihash
2464 newuni = bb.parse.siggen.get_unihash(tid)
2465 # FIXME, need to check it can come from sstate at all for determinism?
2466 remapped = False
2467 if newuni == origuni:
2468 # Nothing to do, we match, skip code below
2469 remapped = True
2470 elif tid in self.scenequeue_covered or tid in self.sq_live:
2471 # Already ran this setscene task or it running. Report the new taskhash
2472 bb.parse.siggen.report_unihash_equiv(tid, newhash, origuni, newuni, self.rqdata.dataCaches)
2473 hashequiv_logger.verbose("Already covered setscene for %s so ignoring rehash (remap)" % (tid))
2474 remapped = True
2475
2476 if not remapped:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002477 #logger.debug("Task %s hash changes: %s->%s %s->%s" % (tid, orighash, newhash, origuni, newuni))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002478 self.rqdata.runtaskentries[tid].hash = newhash
2479 self.rqdata.runtaskentries[tid].unihash = newuni
2480 changed.add(tid)
2481
2482 next |= self.rqdata.runtaskentries[tid].revdeps
2483 total.remove(tid)
2484 next.intersection_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002485
2486 if changed:
2487 for mc in self.rq.worker:
2488 self.rq.worker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2489 for mc in self.rq.fakeworker:
2490 self.rq.fakeworker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2491
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002492 hashequiv_logger.debug(pprint.pformat("Tasks changed:\n%s" % (changed)))
Brad Bishop08902b02019-08-20 09:16:51 -04002493
2494 for tid in changed:
2495 if tid not in self.rqdata.runq_setscene_tids:
2496 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002497 if tid not in self.pending_migrations:
2498 self.pending_migrations.add(tid)
2499
Andrew Geissler82c905d2020-04-13 13:39:40 -05002500 update_tasks = []
Brad Bishop08902b02019-08-20 09:16:51 -04002501 for tid in self.pending_migrations.copy():
Andrew Geissler82c905d2020-04-13 13:39:40 -05002502 if tid in self.runq_running or tid in self.sq_live:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002503 # Too late, task already running, not much we can do now
2504 self.pending_migrations.remove(tid)
2505 continue
2506
Brad Bishop08902b02019-08-20 09:16:51 -04002507 valid = True
2508 # Check no tasks this covers are running
2509 for dep in self.sqdata.sq_covered_tasks[tid]:
2510 if dep in self.runq_running and dep not in self.runq_complete:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002511 hashequiv_logger.debug2("Task %s is running which blocks setscene for %s from running" % (dep, tid))
Brad Bishop08902b02019-08-20 09:16:51 -04002512 valid = False
2513 break
2514 if not valid:
2515 continue
2516
2517 self.pending_migrations.remove(tid)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002518 changed = True
Brad Bishop08902b02019-08-20 09:16:51 -04002519
2520 if tid in self.tasks_scenequeue_done:
2521 self.tasks_scenequeue_done.remove(tid)
2522 for dep in self.sqdata.sq_covered_tasks[tid]:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002523 if dep in self.runq_complete and dep not in self.runq_tasksrun:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002524 bb.error("Task %s marked as completed but now needing to rerun? Halting build." % dep)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002525 self.failed_tids.append(tid)
2526 self.rq.state = runQueueCleanUp
2527 return
2528
Brad Bishop08902b02019-08-20 09:16:51 -04002529 if dep not in self.runq_complete:
2530 if dep in self.tasks_scenequeue_done and dep not in self.sqdata.unskippable:
2531 self.tasks_scenequeue_done.remove(dep)
2532
2533 if tid in self.sq_buildable:
2534 self.sq_buildable.remove(tid)
2535 if tid in self.sq_running:
2536 self.sq_running.remove(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002537 if tid in self.sqdata.outrightfail:
2538 self.sqdata.outrightfail.remove(tid)
2539 if tid in self.scenequeue_notcovered:
2540 self.scenequeue_notcovered.remove(tid)
2541 if tid in self.scenequeue_covered:
2542 self.scenequeue_covered.remove(tid)
2543 if tid in self.scenequeue_notneeded:
2544 self.scenequeue_notneeded.remove(tid)
2545
2546 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2547 self.sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2548
2549 if tid in self.stampcache:
2550 del self.stampcache[tid]
2551
2552 if tid in self.build_stamps:
2553 del self.build_stamps[tid]
2554
2555 update_tasks.append(tid)
2556
2557 update_tasks2 = []
2558 for tid in update_tasks:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002559 harddepfail = False
2560 for t in self.sqdata.sq_harddeps:
2561 if tid in self.sqdata.sq_harddeps[t] and t in self.scenequeue_notcovered:
2562 harddepfail = True
2563 break
2564 if not harddepfail and self.sqdata.sq_revdeps[tid].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
Brad Bishop08902b02019-08-20 09:16:51 -04002565 if tid not in self.sq_buildable:
2566 self.sq_buildable.add(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +00002567 if not self.sqdata.sq_revdeps[tid]:
Brad Bishop08902b02019-08-20 09:16:51 -04002568 self.sq_buildable.add(tid)
2569
Andrew Geissler517393d2023-01-13 08:55:19 -06002570 update_tasks2.append((tid, harddepfail, tid in self.sqdata.valid))
Brad Bishop08902b02019-08-20 09:16:51 -04002571
Andrew Geissler517393d2023-01-13 08:55:19 -06002572 if update_tasks2:
Brad Bishop08902b02019-08-20 09:16:51 -04002573 self.sqdone = False
Patrick Williams92b42cb2022-09-03 06:53:57 -05002574 for mc in sorted(self.sqdata.multiconfigs):
Andrew Geissler517393d2023-01-13 08:55:19 -06002575 for tid in sorted([t[0] for t in update_tasks2]):
Patrick Williams92b42cb2022-09-03 06:53:57 -05002576 if mc_from_tid(tid) != mc:
2577 continue
2578 h = pending_hash_index(tid, self.rqdata)
2579 if h in self.sqdata.hashes and tid != self.sqdata.hashes[h]:
2580 self.sq_deferred[tid] = self.sqdata.hashes[h]
2581 bb.note("Deferring %s after %s" % (tid, self.sqdata.hashes[h]))
Andrew Geissler517393d2023-01-13 08:55:19 -06002582 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 -05002583
Andrew Geissler517393d2023-01-13 08:55:19 -06002584 for (tid, harddepfail, origvalid) in update_tasks2:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002585 if tid in self.sqdata.valid and not origvalid:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002586 hashequiv_logger.verbose("Setscene task %s became valid" % tid)
2587 if harddepfail:
Andrew Geissler517393d2023-01-13 08:55:19 -06002588 logger.debug2("%s has an unavailable hard dependency so skipping" % (tid))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002589 self.sq_task_failoutright(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002590
2591 if changed:
Andrew Geissler5199d832021-09-24 16:47:35 -05002592 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002593 self.holdoff_need_update = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002594
Brad Bishop96ff1982019-08-19 13:50:42 -04002595 def scenequeue_updatecounters(self, task, fail=False):
Brad Bishop08902b02019-08-20 09:16:51 -04002596
2597 for dep in sorted(self.sqdata.sq_deps[task]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002598 if fail and task in self.sqdata.sq_harddeps and dep in self.sqdata.sq_harddeps[task]:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002599 if dep in self.scenequeue_covered or dep in self.scenequeue_notcovered:
2600 # dependency could be already processed, e.g. noexec setscene task
2601 continue
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002602 noexec, stamppresent = check_setscene_stamps(dep, self.rqdata, self.rq, self.stampcache)
2603 if noexec or stamppresent:
2604 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002605 logger.debug2("%s was unavailable and is a hard dependency of %s so skipping" % (task, dep))
Brad Bishop96ff1982019-08-19 13:50:42 -04002606 self.sq_task_failoutright(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002607 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002608 if self.sqdata.sq_revdeps[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2609 if dep not in self.sq_buildable:
2610 self.sq_buildable.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002611
Brad Bishop96ff1982019-08-19 13:50:42 -04002612 next = set([task])
2613 while next:
2614 new = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002615 for t in sorted(next):
Brad Bishop96ff1982019-08-19 13:50:42 -04002616 self.tasks_scenequeue_done.add(t)
2617 # Look down the dependency chain for non-setscene things which this task depends on
2618 # and mark as 'done'
2619 for dep in self.rqdata.runtaskentries[t].depends:
2620 if dep in self.rqdata.runq_setscene_tids or dep in self.tasks_scenequeue_done:
2621 continue
2622 if self.rqdata.runtaskentries[dep].revdeps.issubset(self.tasks_scenequeue_done):
2623 new.add(dep)
Brad Bishop96ff1982019-08-19 13:50:42 -04002624 next = new
2625
Andrew Geissler5199d832021-09-24 16:47:35 -05002626 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002627 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002628
2629 def sq_task_completeoutright(self, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002630 """
2631 Mark a task as completed
2632 Look at the reverse dependencies and mark any task with
2633 completed dependencies as buildable
2634 """
2635
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002636 logger.debug('Found task %s which could be accelerated', task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002637 self.scenequeue_covered.add(task)
2638 self.scenequeue_updatecounters(task)
2639
Brad Bishop96ff1982019-08-19 13:50:42 -04002640 def sq_check_taskfail(self, task):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002641 if self.rqdata.setscene_ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002642 realtask = task.split('_setscene')[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002643 (mc, fn, taskname, taskfn) = split_tid_mcfn(realtask)
2644 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002645 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002646 logger.error('Task %s.%s failed' % (pn, taskname + "_setscene"))
2647 self.rq.state = runQueueCleanUp
2648
Brad Bishop96ff1982019-08-19 13:50:42 -04002649 def sq_task_complete(self, task):
Andrew Geissler5199d832021-09-24 16:47:35 -05002650 bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
Brad Bishop96ff1982019-08-19 13:50:42 -04002651 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002652
Brad Bishop96ff1982019-08-19 13:50:42 -04002653 def sq_task_fail(self, task, result):
Andrew Geissler5199d832021-09-24 16:47:35 -05002654 bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002655 self.scenequeue_notcovered.add(task)
2656 self.scenequeue_updatecounters(task, True)
Brad Bishop96ff1982019-08-19 13:50:42 -04002657 self.sq_check_taskfail(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002658
Brad Bishop96ff1982019-08-19 13:50:42 -04002659 def sq_task_failoutright(self, task):
2660 self.sq_running.add(task)
2661 self.sq_buildable.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002662 self.scenequeue_notcovered.add(task)
2663 self.scenequeue_updatecounters(task, True)
2664
Brad Bishop96ff1982019-08-19 13:50:42 -04002665 def sq_task_skip(self, task):
2666 self.sq_running.add(task)
2667 self.sq_buildable.add(task)
2668 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002669
Brad Bishop96ff1982019-08-19 13:50:42 -04002670 def sq_build_taskdepdata(self, task):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002671 def getsetscenedeps(tid):
2672 deps = set()
2673 (mc, fn, taskname, _) = split_tid_mcfn(tid)
2674 realtid = tid + "_setscene"
2675 idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
2676 for (depname, idependtask) in idepends:
2677 if depname not in self.rqdata.taskData[mc].build_targets:
2678 continue
2679
2680 depfn = self.rqdata.taskData[mc].build_targets[depname][0]
2681 if depfn is None:
2682 continue
2683 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2684 deps.add(deptid)
2685 return deps
2686
2687 taskdepdata = {}
2688 next = getsetscenedeps(task)
2689 next.add(task)
2690 while next:
2691 additional = []
2692 for revdep in next:
2693 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2694 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2695 deps = getsetscenedeps(revdep)
2696 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2697 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002698 unihash = self.rqdata.runtaskentries[revdep].unihash
Patrick Williamsb542dec2023-06-09 01:26:37 -05002699 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2700 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002701 for revdep2 in deps:
2702 if revdep2 not in taskdepdata:
2703 additional.append(revdep2)
2704 next = additional
2705
2706 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2707 return taskdepdata
2708
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002709 def check_setscene_ignore_tasks(self, tid):
2710 # Check task that is going to run against the ignore tasks list
Brad Bishop96ff1982019-08-19 13:50:42 -04002711 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2712 # Ignore covered tasks
2713 if tid in self.tasks_covered:
2714 return False
2715 # Ignore stamped tasks
2716 if self.rq.check_stamp_task(tid, taskname, cache=self.stampcache):
2717 return False
2718 # Ignore noexec tasks
2719 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2720 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2721 return False
2722
2723 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002724 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Brad Bishop96ff1982019-08-19 13:50:42 -04002725 if tid in self.rqdata.runq_setscene_tids:
Andrew Geissler595f6302022-01-24 19:11:47 +00002726 msg = ['Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)]
Brad Bishop96ff1982019-08-19 13:50:42 -04002727 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002728 msg = ['Task %s.%s attempted to execute unexpectedly' % (pn, taskname)]
Andrew Geissler82c905d2020-04-13 13:39:40 -05002729 for t in self.scenequeue_notcovered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002730 msg.append("\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash))
2731 msg.append('\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered))
2732 logger.error("".join(msg))
Brad Bishop96ff1982019-08-19 13:50:42 -04002733 return True
2734 return False
2735
2736class SQData(object):
2737 def __init__(self):
2738 # SceneQueue dependencies
2739 self.sq_deps = {}
2740 # SceneQueue reverse dependencies
2741 self.sq_revdeps = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002742 # Injected inter-setscene task dependencies
2743 self.sq_harddeps = {}
2744 # Cache of stamp files so duplicates can't run in parallel
2745 self.stamps = {}
2746 # Setscene tasks directly depended upon by the build
2747 self.unskippable = set()
2748 # List of setscene tasks which aren't present
2749 self.outrightfail = set()
2750 # A list of normal tasks a setscene task covers
2751 self.sq_covered_tasks = {}
2752
2753def build_scenequeue_data(sqdata, rqdata, rq, cooker, stampcache, sqrq):
2754
2755 sq_revdeps = {}
2756 sq_revdeps_squash = {}
2757 sq_collated_deps = {}
2758
2759 # We need to construct a dependency graph for the setscene functions. Intermediate
2760 # dependencies between the setscene tasks only complicate the code. This code
2761 # therefore aims to collapse the huge runqueue dependency tree into a smaller one
2762 # only containing the setscene functions.
2763
2764 rqdata.init_progress_reporter.next_stage()
2765
2766 # First process the chains up to the first setscene task.
2767 endpoints = {}
2768 for tid in rqdata.runtaskentries:
2769 sq_revdeps[tid] = copy.copy(rqdata.runtaskentries[tid].revdeps)
2770 sq_revdeps_squash[tid] = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00002771 if not sq_revdeps[tid] and tid not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002772 #bb.warn("Added endpoint %s" % (tid))
2773 endpoints[tid] = set()
2774
2775 rqdata.init_progress_reporter.next_stage()
2776
2777 # Secondly process the chains between setscene tasks.
2778 for tid in rqdata.runq_setscene_tids:
2779 sq_collated_deps[tid] = set()
2780 #bb.warn("Added endpoint 2 %s" % (tid))
2781 for dep in rqdata.runtaskentries[tid].depends:
2782 if tid in sq_revdeps[dep]:
2783 sq_revdeps[dep].remove(tid)
2784 if dep not in endpoints:
2785 endpoints[dep] = set()
2786 #bb.warn(" Added endpoint 3 %s" % (dep))
2787 endpoints[dep].add(tid)
2788
2789 rqdata.init_progress_reporter.next_stage()
2790
2791 def process_endpoints(endpoints):
2792 newendpoints = {}
2793 for point, task in endpoints.items():
2794 tasks = set()
2795 if task:
2796 tasks |= task
2797 if sq_revdeps_squash[point]:
2798 tasks |= sq_revdeps_squash[point]
2799 if point not in rqdata.runq_setscene_tids:
2800 for t in tasks:
2801 sq_collated_deps[t].add(point)
2802 sq_revdeps_squash[point] = set()
2803 if point in rqdata.runq_setscene_tids:
2804 sq_revdeps_squash[point] = tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002805 continue
2806 for dep in rqdata.runtaskentries[point].depends:
2807 if point in sq_revdeps[dep]:
2808 sq_revdeps[dep].remove(point)
2809 if tasks:
2810 sq_revdeps_squash[dep] |= tasks
Andrew Geissler595f6302022-01-24 19:11:47 +00002811 if not sq_revdeps[dep] and dep not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002812 newendpoints[dep] = task
Andrew Geissler595f6302022-01-24 19:11:47 +00002813 if newendpoints:
Brad Bishop96ff1982019-08-19 13:50:42 -04002814 process_endpoints(newendpoints)
2815
2816 process_endpoints(endpoints)
2817
2818 rqdata.init_progress_reporter.next_stage()
2819
Brad Bishop08902b02019-08-20 09:16:51 -04002820 # Build a list of tasks which are "unskippable"
2821 # These are direct endpoints referenced by the build upto and including setscene tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002822 # Take the build endpoints (no revdeps) and find the sstate tasks they depend upon
2823 new = True
2824 for tid in rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00002825 if not rqdata.runtaskentries[tid].revdeps:
Brad Bishop96ff1982019-08-19 13:50:42 -04002826 sqdata.unskippable.add(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002827 sqdata.unskippable |= sqrq.cantskip
Brad Bishop96ff1982019-08-19 13:50:42 -04002828 while new:
2829 new = False
Brad Bishop08902b02019-08-20 09:16:51 -04002830 orig = sqdata.unskippable.copy()
2831 for tid in sorted(orig, reverse=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04002832 if tid in rqdata.runq_setscene_tids:
2833 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00002834 if not rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04002835 # These are tasks which have no setscene tasks in their chain, need to mark as directly buildable
Brad Bishop96ff1982019-08-19 13:50:42 -04002836 sqrq.setbuildable(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002837 sqdata.unskippable |= rqdata.runtaskentries[tid].depends
Brad Bishop08902b02019-08-20 09:16:51 -04002838 if sqdata.unskippable != orig:
2839 new = True
2840
2841 sqrq.tasks_scenequeue_done |= sqdata.unskippable.difference(rqdata.runq_setscene_tids)
Brad Bishop96ff1982019-08-19 13:50:42 -04002842
2843 rqdata.init_progress_reporter.next_stage(len(rqdata.runtaskentries))
2844
2845 # Sanity check all dependencies could be changed to setscene task references
2846 for taskcounter, tid in enumerate(rqdata.runtaskentries):
2847 if tid in rqdata.runq_setscene_tids:
2848 pass
Andrew Geissler595f6302022-01-24 19:11:47 +00002849 elif sq_revdeps_squash[tid]:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002850 bb.msg.fatal("RunQueue", "Something went badly wrong during scenequeue generation, halting. Please report this problem.")
Brad Bishop96ff1982019-08-19 13:50:42 -04002851 else:
2852 del sq_revdeps_squash[tid]
2853 rqdata.init_progress_reporter.update(taskcounter)
2854
2855 rqdata.init_progress_reporter.next_stage()
2856
2857 # Resolve setscene inter-task dependencies
2858 # e.g. do_sometask_setscene[depends] = "targetname:do_someothertask_setscene"
2859 # Note that anything explicitly depended upon will have its reverse dependencies removed to avoid circular dependencies
2860 for tid in rqdata.runq_setscene_tids:
2861 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2862 realtid = tid + "_setscene"
2863 idepends = rqdata.taskData[mc].taskentries[realtid].idepends
Andrew Geissler517393d2023-01-13 08:55:19 -06002864 sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2865
Brad Bishop96ff1982019-08-19 13:50:42 -04002866 for (depname, idependtask) in idepends:
2867
2868 if depname not in rqdata.taskData[mc].build_targets:
2869 continue
2870
2871 depfn = rqdata.taskData[mc].build_targets[depname][0]
2872 if depfn is None:
2873 continue
2874 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2875 if deptid not in rqdata.runtaskentries:
2876 bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (realtid, depfn, idependtask))
2877
2878 if not deptid in sqdata.sq_harddeps:
2879 sqdata.sq_harddeps[deptid] = set()
2880 sqdata.sq_harddeps[deptid].add(tid)
2881
2882 sq_revdeps_squash[tid].add(deptid)
2883 # Have to zero this to avoid circular dependencies
2884 sq_revdeps_squash[deptid] = set()
2885
2886 rqdata.init_progress_reporter.next_stage()
2887
2888 for task in sqdata.sq_harddeps:
2889 for dep in sqdata.sq_harddeps[task]:
2890 sq_revdeps_squash[dep].add(task)
2891
2892 rqdata.init_progress_reporter.next_stage()
2893
2894 #for tid in sq_revdeps_squash:
2895 # data = ""
2896 # for dep in sq_revdeps_squash[tid]:
2897 # data = data + "\n %s" % dep
2898 # bb.warn("Task %s_setscene: is %s " % (tid, data))
2899
2900 sqdata.sq_revdeps = sq_revdeps_squash
Brad Bishop96ff1982019-08-19 13:50:42 -04002901 sqdata.sq_covered_tasks = sq_collated_deps
2902
2903 # Build reverse version of revdeps to populate deps structure
2904 for tid in sqdata.sq_revdeps:
2905 sqdata.sq_deps[tid] = set()
2906 for tid in sqdata.sq_revdeps:
2907 for dep in sqdata.sq_revdeps[tid]:
2908 sqdata.sq_deps[dep].add(tid)
2909
2910 rqdata.init_progress_reporter.next_stage()
2911
Brad Bishop00e122a2019-10-05 11:10:57 -04002912 sqdata.multiconfigs = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002913 for tid in sqdata.sq_revdeps:
Brad Bishop00e122a2019-10-05 11:10:57 -04002914 sqdata.multiconfigs.add(mc_from_tid(tid))
Andrew Geissler595f6302022-01-24 19:11:47 +00002915 if not sqdata.sq_revdeps[tid]:
Brad Bishop96ff1982019-08-19 13:50:42 -04002916 sqrq.sq_buildable.add(tid)
2917
2918 rqdata.init_progress_reporter.finish()
2919
Brad Bishop00e122a2019-10-05 11:10:57 -04002920 sqdata.noexec = set()
2921 sqdata.stamppresent = set()
2922 sqdata.valid = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002923
Patrick Williams213cb262021-08-07 19:21:33 -05002924 sqdata.hashes = {}
2925 sqrq.sq_deferred = {}
2926 for mc in sorted(sqdata.multiconfigs):
2927 for tid in sorted(sqdata.sq_revdeps):
2928 if mc_from_tid(tid) != mc:
2929 continue
2930 h = pending_hash_index(tid, rqdata)
2931 if h not in sqdata.hashes:
2932 sqdata.hashes[h] = tid
2933 else:
2934 sqrq.sq_deferred[tid] = sqdata.hashes[h]
2935 bb.note("Deferring %s after %s" % (tid, sqdata.hashes[h]))
2936
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002937 update_scenequeue_data(sqdata.sq_revdeps, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002938
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002939 # Compute a list of 'stale' sstate tasks where the current hash does not match the one
2940 # in any stamp files. Pass the list out to metadata as an event.
2941 found = {}
2942 for tid in rqdata.runq_setscene_tids:
2943 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002944 stamps = bb.build.find_stale_stamps(taskname, taskfn)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002945 if stamps:
2946 if mc not in found:
2947 found[mc] = {}
2948 found[mc][tid] = stamps
2949 for mc in found:
2950 event = bb.event.StaleSetSceneTasks(found[mc])
2951 bb.event.fire(event, cooker.databuilder.mcdata[mc])
2952
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002953def check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=False):
2954
2955 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2956
2957 taskdep = rqdata.dataCaches[mc].task_deps[taskfn]
2958
2959 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Andrew Geissler517393d2023-01-13 08:55:19 -06002960 bb.build.make_stamp_mcfn(taskname + "_setscene", taskfn)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002961 return True, False
2962
2963 if rq.check_stamp_task(tid, taskname + "_setscene", cache=stampcache):
2964 logger.debug2('Setscene stamp current for task %s', tid)
2965 return False, True
2966
2967 if rq.check_stamp_task(tid, taskname, recurse = True, cache=stampcache):
2968 logger.debug2('Normal stamp current for task %s', tid)
2969 return False, True
2970
2971 return False, False
2972
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002973def update_scenequeue_data(tids, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True):
Brad Bishop00e122a2019-10-05 11:10:57 -04002974
2975 tocheck = set()
2976
2977 for tid in sorted(tids):
2978 if tid in sqdata.stamppresent:
2979 sqdata.stamppresent.remove(tid)
2980 if tid in sqdata.valid:
2981 sqdata.valid.remove(tid)
Andrew Geisslerc926e172021-05-07 16:11:35 -05002982 if tid in sqdata.outrightfail:
2983 sqdata.outrightfail.remove(tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04002984
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002985 noexec, stamppresent = check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002986
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002987 if noexec:
Brad Bishop00e122a2019-10-05 11:10:57 -04002988 sqdata.noexec.add(tid)
2989 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002990 logger.debug2("%s is noexec so skipping setscene" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04002991 continue
2992
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002993 if stamppresent:
Brad Bishop00e122a2019-10-05 11:10:57 -04002994 sqdata.stamppresent.add(tid)
2995 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002996 logger.debug2("%s has a valid stamp, skipping" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04002997 continue
2998
2999 tocheck.add(tid)
3000
Brad Bishop1d80a2e2019-11-15 16:35:03 -05003001 sqdata.valid |= rq.validate_hashes(tocheck, cooker.data, len(sqdata.stamppresent), False, summary=summary)
Brad Bishop00e122a2019-10-05 11:10:57 -04003002
Patrick Williams213cb262021-08-07 19:21:33 -05003003 for tid in tids:
3004 if tid in sqdata.stamppresent:
3005 continue
3006 if tid in sqdata.valid:
3007 continue
3008 if tid in sqdata.noexec:
3009 continue
3010 if tid in sqrq.scenequeue_covered:
3011 continue
3012 if tid in sqrq.scenequeue_notcovered:
3013 continue
3014 if tid in sqrq.sq_deferred:
3015 continue
3016 sqdata.outrightfail.add(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003017 logger.debug2("%s already handled (fallthrough), skipping" % (tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04003018
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003019class TaskFailure(Exception):
3020 """
3021 Exception raised when a task in a runqueue fails
3022 """
3023 def __init__(self, x):
3024 self.args = x
3025
3026
3027class runQueueExitWait(bb.event.Event):
3028 """
3029 Event when waiting for task processes to exit
3030 """
3031
3032 def __init__(self, remain):
3033 self.remain = remain
3034 self.message = "Waiting for %s active tasks to finish" % remain
3035 bb.event.Event.__init__(self)
3036
3037class runQueueEvent(bb.event.Event):
3038 """
3039 Base runQueue event class
3040 """
3041 def __init__(self, task, stats, rq):
3042 self.taskid = task
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003043 self.taskstring = task
3044 self.taskname = taskname_from_tid(task)
3045 self.taskfile = fn_from_tid(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003046 self.taskhash = rq.rqdata.get_task_hash(task)
3047 self.stats = stats.copy()
3048 bb.event.Event.__init__(self)
3049
3050class sceneQueueEvent(runQueueEvent):
3051 """
3052 Base sceneQueue event class
3053 """
3054 def __init__(self, task, stats, rq, noexec=False):
3055 runQueueEvent.__init__(self, task, stats, rq)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003056 self.taskstring = task + "_setscene"
3057 self.taskname = taskname_from_tid(task) + "_setscene"
3058 self.taskfile = fn_from_tid(task)
3059 self.taskhash = rq.rqdata.get_task_hash(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003060
3061class runQueueTaskStarted(runQueueEvent):
3062 """
3063 Event notifying a task was started
3064 """
3065 def __init__(self, task, stats, rq, noexec=False):
3066 runQueueEvent.__init__(self, task, stats, rq)
3067 self.noexec = noexec
3068
3069class sceneQueueTaskStarted(sceneQueueEvent):
3070 """
3071 Event notifying a setscene task was started
3072 """
3073 def __init__(self, task, stats, rq, noexec=False):
3074 sceneQueueEvent.__init__(self, task, stats, rq)
3075 self.noexec = noexec
3076
3077class runQueueTaskFailed(runQueueEvent):
3078 """
3079 Event notifying a task failed
3080 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003081 def __init__(self, task, stats, exitcode, rq, fakeroot_log=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003082 runQueueEvent.__init__(self, task, stats, rq)
3083 self.exitcode = exitcode
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003084 self.fakeroot_log = fakeroot_log
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003085
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003086 def __str__(self):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003087 if self.fakeroot_log:
3088 return "Task (%s) failed with exit code '%s' \nPseudo log:\n%s" % (self.taskstring, self.exitcode, self.fakeroot_log)
3089 else:
3090 return "Task (%s) failed with exit code '%s'" % (self.taskstring, self.exitcode)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003091
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003092class sceneQueueTaskFailed(sceneQueueEvent):
3093 """
3094 Event notifying a setscene task failed
3095 """
3096 def __init__(self, task, stats, exitcode, rq):
3097 sceneQueueEvent.__init__(self, task, stats, rq)
3098 self.exitcode = exitcode
3099
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003100 def __str__(self):
3101 return "Setscene task (%s) failed with exit code '%s' - real task will be run instead" % (self.taskstring, self.exitcode)
3102
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003103class sceneQueueComplete(sceneQueueEvent):
3104 """
3105 Event when all the sceneQueue tasks are complete
3106 """
3107 def __init__(self, stats, rq):
3108 self.stats = stats.copy()
3109 bb.event.Event.__init__(self)
3110
3111class runQueueTaskCompleted(runQueueEvent):
3112 """
3113 Event notifying a task completed
3114 """
3115
3116class sceneQueueTaskCompleted(sceneQueueEvent):
3117 """
3118 Event notifying a setscene task completed
3119 """
3120
3121class runQueueTaskSkipped(runQueueEvent):
3122 """
3123 Event notifying a task was skipped
3124 """
3125 def __init__(self, task, stats, rq, reason):
3126 runQueueEvent.__init__(self, task, stats, rq)
3127 self.reason = reason
3128
Brad Bishop08902b02019-08-20 09:16:51 -04003129class taskUniHashUpdate(bb.event.Event):
3130 """
3131 Base runQueue event class
3132 """
3133 def __init__(self, task, unihash):
3134 self.taskid = task
3135 self.unihash = unihash
3136 bb.event.Event.__init__(self)
3137
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003138class runQueuePipe():
3139 """
3140 Abstraction for a pipe between a worker thread and the server
3141 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003142 def __init__(self, pipein, pipeout, d, rq, rqexec, fakerootlogs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003143 self.input = pipein
3144 if pipeout:
3145 pipeout.close()
3146 bb.utils.nonblockingfd(self.input)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003147 self.queue = b""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003148 self.d = d
3149 self.rq = rq
3150 self.rqexec = rqexec
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003151 self.fakerootlogs = fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003152
3153 def setrunqueueexec(self, rqexec):
3154 self.rqexec = rqexec
3155
3156 def read(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003157 for workers, name in [(self.rq.worker, "Worker"), (self.rq.fakeworker, "Fakeroot")]:
3158 for worker in workers.values():
3159 worker.process.poll()
3160 if worker.process.returncode is not None and not self.rq.teardown:
3161 bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, worker.process.pid, str(worker.process.returncode)))
3162 self.rq.finish_runqueue(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003163
3164 start = len(self.queue)
3165 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003166 self.queue = self.queue + (self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003167 except (OSError, IOError) as e:
3168 if e.errno != errno.EAGAIN:
3169 raise
3170 end = len(self.queue)
3171 found = True
Andrew Geissler595f6302022-01-24 19:11:47 +00003172 while found and self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003173 found = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003174 index = self.queue.find(b"</event>")
3175 while index != -1 and self.queue.startswith(b"<event>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003176 try:
3177 event = pickle.loads(self.queue[7:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003178 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
3179 if isinstance(e, pickle.UnpicklingError) and "truncated" in str(e):
3180 # The pickled data could contain "</event>" so search for the next occurance
3181 # unpickling again, this should be the only way an unpickle error could occur
3182 index = self.queue.find(b"</event>", index + 1)
3183 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003184 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
3185 bb.event.fire_from_worker(event, self.d)
Brad Bishop08902b02019-08-20 09:16:51 -04003186 if isinstance(event, taskUniHashUpdate):
3187 self.rqexec.updated_taskhash_queue.append((event.taskid, event.unihash))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003188 found = True
3189 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003190 index = self.queue.find(b"</event>")
3191 index = self.queue.find(b"</exitcode>")
3192 while index != -1 and self.queue.startswith(b"<exitcode>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003193 try:
3194 task, status = pickle.loads(self.queue[10:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003195 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003196 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003197 (_, _, _, taskfn) = split_tid_mcfn(task)
3198 fakerootlog = None
3199 if self.fakerootlogs and taskfn and taskfn in self.fakerootlogs:
3200 fakerootlog = self.fakerootlogs[taskfn]
3201 self.rqexec.runqueue_process_waitpid(task, status, fakerootlog=fakerootlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003202 found = True
3203 self.queue = self.queue[index+11:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003204 index = self.queue.find(b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003205 return (end > start)
3206
3207 def close(self):
3208 while self.read():
3209 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00003210 if self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003211 print("Warning, worker left partial message: %s" % self.queue)
3212 self.input.close()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003213
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003214def get_setscene_enforce_ignore_tasks(d, targets):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003215 if d.getVar('BB_SETSCENE_ENFORCE') != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003216 return None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003217 ignore_tasks = (d.getVar("BB_SETSCENE_ENFORCE_IGNORE_TASKS") or "").split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003218 outlist = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003219 for item in ignore_tasks[:]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003220 if item.startswith('%:'):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003221 for (mc, target, task, fn) in targets:
3222 outlist.append(target + ':' + item.split(':')[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003223 else:
3224 outlist.append(item)
3225 return outlist
3226
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003227def check_setscene_enforce_ignore_tasks(pn, taskname, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003228 import fnmatch
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003229 if ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003230 item = '%s:%s' % (pn, taskname)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003231 for ignore_tasks in ignore_tasks:
3232 if fnmatch.fnmatch(item, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003233 return True
3234 return False
3235 return True