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