blob: 02d7ff97682d955fce0e50073a9ef4a0c3cd6197 [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:
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600160 self.buildable.add(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
Andrew Geissler5082cc72023-09-11 08:41:39 -0400203 psi_accumulation_interval = 1.0
204 cpu_pressure = (float(curr_cpu_pressure) - float(self.prev_cpu_pressure)) / tdiff
205 io_pressure = (float(curr_io_pressure) - float(self.prev_io_pressure)) / tdiff
206 memory_pressure = (float(curr_memory_pressure) - float(self.prev_memory_pressure)) / tdiff
207 exceeds_cpu_pressure = self.rq.max_cpu_pressure and cpu_pressure > self.rq.max_cpu_pressure
208 exceeds_io_pressure = self.rq.max_io_pressure and io_pressure > self.rq.max_io_pressure
209 exceeds_memory_pressure = self.rq.max_memory_pressure and memory_pressure > self.rq.max_memory_pressure
210
211 if tdiff > psi_accumulation_interval:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500212 self.prev_cpu_pressure = curr_cpu_pressure
213 self.prev_io_pressure = curr_io_pressure
Patrick Williams92b42cb2022-09-03 06:53:57 -0500214 self.prev_memory_pressure = curr_memory_pressure
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500215 self.prev_pressure_time = now
Andrew Geissler5082cc72023-09-11 08:41:39 -0400216
Andrew Geissler8f840682023-07-21 09:09:43 -0500217 pressure_state = (exceeds_cpu_pressure, exceeds_io_pressure, exceeds_memory_pressure)
Andrew Geissler5082cc72023-09-11 08:41:39 -0400218 pressure_values = (round(cpu_pressure,1), self.rq.max_cpu_pressure, round(io_pressure,1), self.rq.max_io_pressure, round(memory_pressure,1), self.rq.max_memory_pressure)
Andrew Geissler8f840682023-07-21 09:09:43 -0500219 if hasattr(self, "pressure_state") and pressure_state != self.pressure_state:
Andrew Geissler5082cc72023-09-11 08:41:39 -0400220 bb.note("Pressure status changed to CPU: %s, IO: %s, Mem: %s (CPU: %s/%s, IO: %s/%s, Mem: %s/%s) - using %s/%s bitbake threads" % (pressure_state + pressure_values + (len(self.rq.runq_running.difference(self.rq.runq_complete)), self.rq.number_tasks)))
Andrew Geissler8f840682023-07-21 09:09:43 -0500221 self.pressure_state = pressure_state
Patrick Williams92b42cb2022-09-03 06:53:57 -0500222 return (exceeds_cpu_pressure or exceeds_io_pressure or exceeds_memory_pressure)
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500223 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500224
225 def next_buildable_task(self):
226 """
227 Return the id of the first task we find that is buildable
228 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500229 # Once tasks are running we don't need to worry about them again
230 self.buildable.difference_update(self.rq.runq_running)
Brad Bishop08902b02019-08-20 09:16:51 -0400231 buildable = set(self.buildable)
Brad Bishop08902b02019-08-20 09:16:51 -0400232 buildable.difference_update(self.rq.holdoff_tasks)
233 buildable.intersection_update(self.rq.tasks_covered | self.rq.tasks_notcovered)
Brad Bishop96ff1982019-08-19 13:50:42 -0400234 if not buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500235 return None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800236
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500237 # Bitbake requires that at least one task be active. Only check for pressure if
238 # this is the case, otherwise the pressure limitation could result in no tasks
239 # being active and no new tasks started thereby, at times, breaking the scheduler.
240 if self.rq.stats.active and self.exceeds_max_pressure():
241 return None
242
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800243 # Filter out tasks that have a max number of threads that have been exceeded
244 skip_buildable = {}
245 for running in self.rq.runq_running.difference(self.rq.runq_complete):
246 rtaskname = taskname_from_tid(running)
247 if rtaskname not in self.skip_maxthread:
248 self.skip_maxthread[rtaskname] = self.rq.cfgData.getVarFlag(rtaskname, "number_threads")
249 if not self.skip_maxthread[rtaskname]:
250 continue
251 if rtaskname in skip_buildable:
252 skip_buildable[rtaskname] += 1
253 else:
254 skip_buildable[rtaskname] = 1
255
Brad Bishop96ff1982019-08-19 13:50:42 -0400256 if len(buildable) == 1:
Brad Bishop08902b02019-08-20 09:16:51 -0400257 tid = buildable.pop()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800258 taskname = taskname_from_tid(tid)
259 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
260 return None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600261 stamp = self.stamps[tid]
262 if stamp not in self.rq.build_stamps.values():
263 return tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500264
265 if not self.rev_prio_map:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600266 self.rev_prio_map = {}
267 for tid in self.rqdata.runtaskentries:
268 self.rev_prio_map[tid] = self.prio_map.index(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500269
270 best = None
271 bestprio = None
Brad Bishop96ff1982019-08-19 13:50:42 -0400272 for tid in buildable:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800273 taskname = taskname_from_tid(tid)
274 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
275 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600276 prio = self.rev_prio_map[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500277 if bestprio is None or bestprio > prio:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600278 stamp = self.stamps[tid]
279 if stamp in self.rq.build_stamps.values():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500280 continue
281 bestprio = prio
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600282 best = tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283
284 return best
285
286 def next(self):
287 """
288 Return the id of the task we should build next
289 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800290 if self.rq.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500291 return self.next_buildable_task()
292
Brad Bishop316dfdd2018-06-25 12:45:53 -0400293 def newbuildable(self, task):
Brad Bishop08902b02019-08-20 09:16:51 -0400294 self.buildable.add(task)
295
296 def removebuildable(self, task):
297 self.buildable.remove(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500298
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500299 def describe_task(self, taskid):
300 result = 'ID %s' % taskid
301 if self.rev_prio_map:
302 result = result + (' pri %d' % self.rev_prio_map[taskid])
303 return result
304
305 def dump_prio(self, comment):
306 bb.debug(3, '%s (most important first):\n%s' %
307 (comment,
308 '\n'.join(['%d. %s' % (index + 1, self.describe_task(taskid)) for
309 index, taskid in enumerate(self.prio_map)])))
310
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311class RunQueueSchedulerSpeed(RunQueueScheduler):
312 """
313 A scheduler optimised for speed. The priority map is sorted by task weight,
314 heavier weighted tasks (tasks needed by the most other tasks) are run first.
315 """
316 name = "speed"
317
318 def __init__(self, runqueue, rqdata):
319 """
320 The priority map is sorted by task weight.
321 """
322 RunQueueScheduler.__init__(self, runqueue, rqdata)
323
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600324 weights = {}
325 for tid in self.rqdata.runtaskentries:
326 weight = self.rqdata.runtaskentries[tid].weight
327 if not weight in weights:
328 weights[weight] = []
329 weights[weight].append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500330
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600331 self.prio_map = []
332 for weight in sorted(weights):
333 for w in weights[weight]:
334 self.prio_map.append(w)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500335
336 self.prio_map.reverse()
337
338class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed):
339 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500340 A scheduler optimised to complete .bb files as quickly as possible. The
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500341 priority map is sorted by task weight, but then reordered so once a given
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500342 .bb file starts to build, it's completed as quickly as possible by
343 running all tasks related to the same .bb file one after the after.
344 This works well where disk space is at a premium and classes like OE's
345 rm_work are in force.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500346 """
347 name = "completion"
348
349 def __init__(self, runqueue, rqdata):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500350 super(RunQueueSchedulerCompletion, self).__init__(runqueue, rqdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500352 # Extract list of tasks for each recipe, with tasks sorted
353 # ascending from "must run first" (typically do_fetch) to
354 # "runs last" (do_build). The speed scheduler prioritizes
355 # tasks that must run first before the ones that run later;
356 # this is what we depend on here.
357 task_lists = {}
358 for taskid in self.prio_map:
359 fn, taskname = taskid.rsplit(':', 1)
360 task_lists.setdefault(fn, []).append(taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500361
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500362 # Now unify the different task lists. The strategy is that
363 # common tasks get skipped and new ones get inserted after the
364 # preceeding common one(s) as they are found. Because task
365 # lists should differ only by their number of tasks, but not
366 # the ordering of the common tasks, this should result in a
367 # deterministic result that is a superset of the individual
368 # task ordering.
369 all_tasks = []
370 for recipe, new_tasks in task_lists.items():
371 index = 0
372 old_task = all_tasks[index] if index < len(all_tasks) else None
373 for new_task in new_tasks:
374 if old_task == new_task:
375 # Common task, skip it. This is the fast-path which
376 # avoids a full search.
377 index += 1
378 old_task = all_tasks[index] if index < len(all_tasks) else None
379 else:
380 try:
381 index = all_tasks.index(new_task)
382 # Already present, just not at the current
383 # place. We re-synchronized by changing the
384 # index so that it matches again. Now
385 # move on to the next existing task.
386 index += 1
387 old_task = all_tasks[index] if index < len(all_tasks) else None
388 except ValueError:
389 # Not present. Insert before old_task, which
390 # remains the same (but gets shifted back).
391 all_tasks.insert(index, new_task)
392 index += 1
393 bb.debug(3, 'merged task list: %s' % all_tasks)
394
395 # Now reverse the order so that tasks that finish the work on one
396 # recipe are considered more imporant (= come first). The ordering
397 # is now so that do_build is most important.
398 all_tasks.reverse()
399
400 # Group tasks of the same kind before tasks of less important
401 # kinds at the head of the queue (because earlier = lower
402 # priority number = runs earlier), while preserving the
403 # ordering by recipe. If recipe foo is more important than
404 # bar, then the goal is to work on foo's do_populate_sysroot
405 # before bar's do_populate_sysroot and on the more important
406 # tasks of foo before any of the less important tasks in any
407 # other recipe (if those other recipes are more important than
408 # foo).
409 #
410 # All of this only applies when tasks are runable. Explicit
411 # dependencies still override this ordering by priority.
412 #
413 # Here's an example why this priority re-ordering helps with
414 # minimizing disk usage. Consider a recipe foo with a higher
415 # priority than bar where foo DEPENDS on bar. Then the
416 # implicit rule (from base.bbclass) is that foo's do_configure
417 # depends on bar's do_populate_sysroot. This ensures that
418 # bar's do_populate_sysroot gets done first. Normally the
419 # tasks from foo would continue to run once that is done, and
420 # bar only gets completed and cleaned up later. By ordering
421 # bar's task that depend on bar's do_populate_sysroot before foo's
422 # do_configure, that problem gets avoided.
423 task_index = 0
424 self.dump_prio('original priorities')
425 for task in all_tasks:
426 for index in range(task_index, self.numTasks):
427 taskid = self.prio_map[index]
428 taskname = taskid.rsplit(':', 1)[1]
429 if taskname == task:
430 del self.prio_map[index]
431 self.prio_map.insert(task_index, taskid)
432 task_index += 1
433 self.dump_prio('completion priorities')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500434
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600435class RunTaskEntry(object):
436 def __init__(self):
437 self.depends = set()
438 self.revdeps = set()
439 self.hash = None
Brad Bishop19323692019-04-05 15:28:33 -0400440 self.unihash = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600441 self.task = None
442 self.weight = 1
443
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444class RunQueueData:
445 """
446 BitBake Run Queue implementation
447 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600448 def __init__(self, rq, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500449 self.cooker = cooker
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600450 self.dataCaches = dataCaches
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500451 self.taskData = taskData
452 self.targets = targets
453 self.rq = rq
454 self.warn_multi_bb = False
455
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000456 self.multi_provider_allowed = (cfgData.getVar("BB_MULTI_PROVIDER_ALLOWED") or "").split()
457 self.setscene_ignore_tasks = get_setscene_enforce_ignore_tasks(cfgData, targets)
458 self.setscene_ignore_tasks_checked = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500459 self.setscene_enforce = (cfgData.getVar('BB_SETSCENE_ENFORCE') == "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600460 self.init_progress_reporter = bb.progress.DummyMultiStageProcessProgressReporter()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500461
462 self.reset()
463
464 def reset(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600465 self.runtaskentries = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500466
467 def runq_depends_names(self, ids):
468 import re
469 ret = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600470 for id in ids:
471 nam = os.path.basename(id)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500472 nam = re.sub("_[^,]*,", ",", nam)
473 ret.extend([nam])
474 return ret
475
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600476 def get_task_hash(self, tid):
477 return self.runtaskentries[tid].hash
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500478
Brad Bishop19323692019-04-05 15:28:33 -0400479 def get_task_unihash(self, tid):
480 return self.runtaskentries[tid].unihash
481
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600482 def get_user_idstring(self, tid, task_name_suffix = ""):
483 return tid + task_name_suffix
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500484
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500485 def get_short_user_idstring(self, task, task_name_suffix = ""):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500486 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
487 pn = self.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600488 taskname = taskname_from_tid(task) + task_name_suffix
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500489 return "%s:%s" % (pn, taskname)
490
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491 def circular_depchains_handler(self, tasks):
492 """
493 Some tasks aren't buildable, likely due to circular dependency issues.
494 Identify the circular dependencies and print them in a user readable format.
495 """
496 from copy import deepcopy
497
498 valid_chains = []
499 explored_deps = {}
500 msgs = []
501
Andrew Geissler99467da2019-02-25 18:54:23 -0600502 class TooManyLoops(Exception):
503 pass
504
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500505 def chain_reorder(chain):
506 """
507 Reorder a dependency chain so the lowest task id is first
508 """
509 lowest = 0
510 new_chain = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600511 for entry in range(len(chain)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500512 if chain[entry] < chain[lowest]:
513 lowest = entry
514 new_chain.extend(chain[lowest:])
515 new_chain.extend(chain[:lowest])
516 return new_chain
517
518 def chain_compare_equal(chain1, chain2):
519 """
520 Compare two dependency chains and see if they're the same
521 """
522 if len(chain1) != len(chain2):
523 return False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600524 for index in range(len(chain1)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500525 if chain1[index] != chain2[index]:
526 return False
527 return True
528
529 def chain_array_contains(chain, chain_array):
530 """
531 Return True if chain_array contains chain
532 """
533 for ch in chain_array:
534 if chain_compare_equal(ch, chain):
535 return True
536 return False
537
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600538 def find_chains(tid, prev_chain):
539 prev_chain.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500540 total_deps = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600541 total_deps.extend(self.runtaskentries[tid].revdeps)
542 for revdep in self.runtaskentries[tid].revdeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500543 if revdep in prev_chain:
544 idx = prev_chain.index(revdep)
545 # To prevent duplicates, reorder the chain to start with the lowest taskid
546 # and search through an array of those we've already printed
547 chain = prev_chain[idx:]
548 new_chain = chain_reorder(chain)
549 if not chain_array_contains(new_chain, valid_chains):
550 valid_chains.append(new_chain)
551 msgs.append("Dependency loop #%d found:\n" % len(valid_chains))
552 for dep in new_chain:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600553 msgs.append(" Task %s (dependent Tasks %s)\n" % (dep, self.runq_depends_names(self.runtaskentries[dep].depends)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500554 msgs.append("\n")
555 if len(valid_chains) > 10:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000556 msgs.append("Halted dependency loops search after 10 matches.\n")
Andrew Geissler99467da2019-02-25 18:54:23 -0600557 raise TooManyLoops
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500558 continue
559 scan = False
560 if revdep not in explored_deps:
561 scan = True
562 elif revdep in explored_deps[revdep]:
563 scan = True
564 else:
565 for dep in prev_chain:
566 if dep in explored_deps[revdep]:
567 scan = True
568 if scan:
569 find_chains(revdep, copy.deepcopy(prev_chain))
570 for dep in explored_deps[revdep]:
571 if dep not in total_deps:
572 total_deps.append(dep)
573
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600574 explored_deps[tid] = total_deps
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500575
Andrew Geissler99467da2019-02-25 18:54:23 -0600576 try:
577 for task in tasks:
578 find_chains(task, [])
579 except TooManyLoops:
580 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500581
582 return msgs
583
584 def calculate_task_weights(self, endpoints):
585 """
586 Calculate a number representing the "weight" of each task. Heavier weighted tasks
587 have more dependencies and hence should be executed sooner for maximum speed.
588
589 This function also sanity checks the task list finding tasks that are not
590 possible to execute due to circular dependencies.
591 """
592
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600593 numTasks = len(self.runtaskentries)
594 weight = {}
595 deps_left = {}
596 task_done = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600598 for tid in self.runtaskentries:
599 task_done[tid] = False
600 weight[tid] = 1
601 deps_left[tid] = len(self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600603 for tid in endpoints:
604 weight[tid] = 10
605 task_done[tid] = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500606
607 while True:
608 next_points = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600609 for tid in endpoints:
610 for revdep in self.runtaskentries[tid].depends:
611 weight[revdep] = weight[revdep] + weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500612 deps_left[revdep] = deps_left[revdep] - 1
613 if deps_left[revdep] == 0:
614 next_points.append(revdep)
615 task_done[revdep] = True
616 endpoints = next_points
Andrew Geissler595f6302022-01-24 19:11:47 +0000617 if not next_points:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500618 break
619
620 # Circular dependency sanity check
621 problem_tasks = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600622 for tid in self.runtaskentries:
623 if task_done[tid] is False or deps_left[tid] != 0:
624 problem_tasks.append(tid)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600625 logger.debug2("Task %s is not buildable", tid)
626 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 -0600627 self.runtaskentries[tid].weight = weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500628
629 if problem_tasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600630 message = "%s unbuildable tasks were found.\n" % len(problem_tasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631 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"
632 message = message + "Identifying dependency loops (this may take a short while)...\n"
633 logger.error(message)
634
635 msgs = self.circular_depchains_handler(problem_tasks)
636
637 message = "\n"
638 for msg in msgs:
639 message = message + msg
640 bb.msg.fatal("RunQueue", message)
641
642 return weight
643
644 def prepare(self):
645 """
646 Turn a set of taskData into a RunQueue and compute data needed
647 to optimise the execution order.
648 """
649
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600650 runq_build = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 recursivetasks = {}
652 recursiveitasks = {}
653 recursivetasksselfref = set()
654
655 taskData = self.taskData
656
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600657 found = False
658 for mc in self.taskData:
Andrew Geissler595f6302022-01-24 19:11:47 +0000659 if taskData[mc].taskentries:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600660 found = True
661 break
662 if not found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663 # Nothing to do
664 return 0
665
Andrew Geissler517393d2023-01-13 08:55:19 -0600666 bb.parse.siggen.setup_datacache(self.dataCaches)
667
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600668 self.init_progress_reporter.start()
669 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600670 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500671
672 # Step A - Work out a list of tasks to run
673 #
674 # Taskdata gives us a list of possible providers for every build and run
675 # target ordered by priority. It also gives information on each of those
676 # providers.
677 #
678 # To create the actual list of tasks to execute we fix the list of
679 # providers and then resolve the dependencies into task IDs. This
680 # process is repeated for each type of dependency (tdepends, deptask,
681 # rdeptast, recrdeptask, idepends).
682
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600683 def add_build_dependencies(depids, tasknames, depends, mc):
684 for depname in depids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500685 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600686 if depname not in taskData[mc].build_targets or not taskData[mc].build_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500687 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600688 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689 if depdata is None:
690 continue
691 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600692 t = depdata + ":" + taskname
693 if t in taskData[mc].taskentries:
694 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500695
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600696 def add_runtime_dependencies(depids, tasknames, depends, mc):
697 for depname in depids:
698 if depname not in taskData[mc].run_targets or not taskData[mc].run_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500699 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600700 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500701 if depdata is None:
702 continue
703 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600704 t = depdata + ":" + taskname
705 if t in taskData[mc].taskentries:
706 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800708 def add_mc_dependencies(mc, tid):
709 mcdeps = taskData[mc].get_mcdepends()
710 for dep in mcdeps:
711 mcdependency = dep.split(':')
712 pn = mcdependency[3]
713 frommc = mcdependency[1]
714 mcdep = mcdependency[2]
715 deptask = mcdependency[4]
Andrew Geissler517393d2023-01-13 08:55:19 -0600716 if mcdep not in taskData:
717 bb.fatal("Multiconfig '%s' is referenced in multiconfig dependency '%s' but not enabled in BBMULTICONFIG?" % (mcdep, dep))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800718 if mc == frommc:
719 fn = taskData[mcdep].build_targets[pn][0]
720 newdep = '%s:%s' % (fn,deptask)
721 taskData[mc].taskentries[tid].tdepends.append(newdep)
722
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 for mc in taskData:
724 for tid in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600726 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
727 #runtid = build_tid(mc, fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500728
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600729 #logger.debug2("Processing %s,%s:%s", mc, fn, taskname)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600730
731 depends = set()
732 task_deps = self.dataCaches[mc].task_deps[taskfn]
733
734 self.runtaskentries[tid] = RunTaskEntry()
735
736 if fn in taskData[mc].failed_fns:
737 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500738
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800739 # We add multiconfig dependencies before processing internal task deps (tdepends)
740 if 'mcdepends' in task_deps and taskname in task_deps['mcdepends']:
741 add_mc_dependencies(mc, tid)
742
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500743 # Resolve task internal dependencies
744 #
745 # e.g. addtask before X after Y
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600746 for t in taskData[mc].taskentries[tid].tdepends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800747 (depmc, depfn, deptaskname, _) = split_tid_mcfn(t)
748 depends.add(build_tid(depmc, depfn, deptaskname))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749
750 # Resolve 'deptask' dependencies
751 #
752 # e.g. do_sometask[deptask] = "do_someothertask"
753 # (makes sure sometask runs after someothertask of all DEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600754 if 'deptask' in task_deps and taskname in task_deps['deptask']:
755 tasknames = task_deps['deptask'][taskname].split()
756 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500757
758 # Resolve 'rdeptask' dependencies
759 #
760 # e.g. do_sometask[rdeptask] = "do_someothertask"
761 # (makes sure sometask runs after someothertask of all RDEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600762 if 'rdeptask' in task_deps and taskname in task_deps['rdeptask']:
763 tasknames = task_deps['rdeptask'][taskname].split()
764 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500765
766 # Resolve inter-task dependencies
767 #
768 # e.g. do_sometask[depends] = "targetname:do_someothertask"
769 # (makes sure sometask runs after targetname's someothertask)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600770 idepends = taskData[mc].taskentries[tid].idepends
771 for (depname, idependtask) in idepends:
772 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 -0500773 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600774 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600776 t = depdata + ":" + idependtask
777 depends.add(t)
778 if t not in taskData[mc].taskentries:
779 bb.msg.fatal("RunQueue", "Task %s in %s depends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
780 irdepends = taskData[mc].taskentries[tid].irdepends
781 for (depname, idependtask) in irdepends:
782 if depname in taskData[mc].run_targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500783 # Won't be in run_targets if ASSUME_PROVIDED
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500784 if not taskData[mc].run_targets[depname]:
785 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600786 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600788 t = depdata + ":" + idependtask
789 depends.add(t)
790 if t not in taskData[mc].taskentries:
791 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 -0500792
793 # Resolve recursive 'recrdeptask' dependencies (Part A)
794 #
795 # e.g. do_sometask[recrdeptask] = "do_someothertask"
796 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
797 # We cover the recursive part of the dependencies below
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 if 'recrdeptask' in task_deps and taskname in task_deps['recrdeptask']:
799 tasknames = task_deps['recrdeptask'][taskname].split()
800 recursivetasks[tid] = tasknames
801 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
802 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
803 if taskname in tasknames:
804 recursivetasksselfref.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500805
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600806 if 'recideptask' in task_deps and taskname in task_deps['recideptask']:
807 recursiveitasks[tid] = []
808 for t in task_deps['recideptask'][taskname].split():
809 newdep = build_tid(mc, fn, t)
810 recursiveitasks[tid].append(newdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500811
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600812 self.runtaskentries[tid].depends = depends
Brad Bishop316dfdd2018-06-25 12:45:53 -0400813 # Remove all self references
814 self.runtaskentries[tid].depends.discard(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500815
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600816 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500817
Brad Bishop316dfdd2018-06-25 12:45:53 -0400818 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600819 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400820
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821 # Resolve recursive 'recrdeptask' dependencies (Part B)
822 #
823 # e.g. do_sometask[recrdeptask] = "do_someothertask"
824 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600825 # 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 -0600826
Brad Bishop316dfdd2018-06-25 12:45:53 -0400827 # Generating/interating recursive lists of dependencies is painful and potentially slow
828 # Precompute recursive task dependencies here by:
829 # a) create a temp list of reverse dependencies (revdeps)
830 # b) walk up the ends of the chains (when a given task no longer has dependencies i.e. len(deps) == 0)
831 # c) combine the total list of dependencies in cumulativedeps
832 # d) optimise by pre-truncating 'task' off the items in cumulativedeps (keeps items in sets lower)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834
Brad Bishop316dfdd2018-06-25 12:45:53 -0400835 revdeps = {}
836 deps = {}
837 cumulativedeps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600838 for tid in self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400839 deps[tid] = set(self.runtaskentries[tid].depends)
840 revdeps[tid] = set()
841 cumulativedeps[tid] = set()
842 # Generate a temp list of reverse dependencies
843 for tid in self.runtaskentries:
844 for dep in self.runtaskentries[tid].depends:
845 revdeps[dep].add(tid)
846 # Find the dependency chain endpoints
847 endpoints = set()
848 for tid in self.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +0000849 if not deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400850 endpoints.add(tid)
851 # Iterate the chains collating dependencies
852 while endpoints:
853 next = set()
854 for tid in endpoints:
855 for dep in revdeps[tid]:
856 cumulativedeps[dep].add(fn_from_tid(tid))
857 cumulativedeps[dep].update(cumulativedeps[tid])
858 if tid in deps[dep]:
859 deps[dep].remove(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +0000860 if not deps[dep]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400861 next.add(dep)
862 endpoints = next
863 #for tid in deps:
Andrew Geissler595f6302022-01-24 19:11:47 +0000864 # if deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400865 # bb.warn("Sanity test failure, dependencies left for %s (%s)" % (tid, deps[tid]))
866
867 # Loop here since recrdeptasks can depend upon other recrdeptasks and we have to
868 # resolve these recursively until we aren't adding any further extra dependencies
869 extradeps = True
870 while extradeps:
871 extradeps = 0
872 for tid in recursivetasks:
873 tasknames = recursivetasks[tid]
874
875 totaldeps = set(self.runtaskentries[tid].depends)
876 if tid in recursiveitasks:
877 totaldeps.update(recursiveitasks[tid])
878 for dep in recursiveitasks[tid]:
879 if dep not in self.runtaskentries:
880 continue
881 totaldeps.update(self.runtaskentries[dep].depends)
882
883 deps = set()
884 for dep in totaldeps:
885 if dep in cumulativedeps:
886 deps.update(cumulativedeps[dep])
887
888 for t in deps:
889 for taskname in tasknames:
890 newtid = t + ":" + taskname
891 if newtid == tid:
892 continue
893 if newtid in self.runtaskentries and newtid not in self.runtaskentries[tid].depends:
894 extradeps += 1
895 self.runtaskentries[tid].depends.add(newtid)
896
897 # Handle recursive tasks which depend upon other recursive tasks
898 deps = set()
899 for dep in self.runtaskentries[tid].depends.intersection(recursivetasks):
900 deps.update(self.runtaskentries[dep].depends.difference(self.runtaskentries[tid].depends))
901 for newtid in deps:
902 for taskname in tasknames:
903 if not newtid.endswith(":" + taskname):
904 continue
905 if newtid in self.runtaskentries:
906 extradeps += 1
907 self.runtaskentries[tid].depends.add(newtid)
908
909 bb.debug(1, "Added %s recursive dependencies in this loop" % extradeps)
910
911 # Remove recrdeptask circular references so that do_a[recrdeptask] = "do_a do_b" can work
912 for tid in recursivetasksselfref:
913 self.runtaskentries[tid].depends.difference_update(recursivetasksselfref)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600914
915 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600916 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600917
918 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500919
920 # Step B - Mark all active tasks
921 #
922 # Start with the tasks we were asked to run and mark all dependencies
923 # as active too. If the task is to be 'forced', clear its stamp. Once
924 # all active tasks are marked, prune the ones we don't need.
925
926 logger.verbose("Marking Active Tasks")
927
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600928 def mark_active(tid, depth):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500929 """
930 Mark an item as active along with its depends
931 (calls itself recursively)
932 """
933
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600934 if tid in runq_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500935 return
936
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600937 runq_build[tid] = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500938
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600939 depends = self.runtaskentries[tid].depends
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500940 for depend in depends:
941 mark_active(depend, depth+1)
942
Brad Bishop79641f22019-09-10 07:20:22 -0400943 def invalidate_task(tid, error_nostamp):
944 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
945 taskdep = self.dataCaches[mc].task_deps[taskfn]
946 if fn + ":" + taskname not in taskData[mc].taskentries:
947 logger.warning("Task %s does not exist, invalidating this task will have no effect" % taskname)
948 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
949 if error_nostamp:
950 bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
951 else:
952 bb.debug(1, "Task %s is marked nostamp, cannot invalidate this task" % taskname)
953 else:
954 logger.verbose("Invalidate task %s, %s", taskname, fn)
Andrew Geissler517393d2023-01-13 08:55:19 -0600955 bb.parse.siggen.invalidate_task(taskname, taskfn)
Brad Bishop79641f22019-09-10 07:20:22 -0400956
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600957 self.target_tids = []
958 for (mc, target, task, fn) in self.targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500959
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600960 if target not in taskData[mc].build_targets or not taskData[mc].build_targets[target]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500961 continue
962
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600963 if target in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500964 continue
965
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500966 parents = False
967 if task.endswith('-'):
968 parents = True
969 task = task[:-1]
970
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600971 if fn in taskData[mc].failed_fns:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500972 continue
973
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600974 # fn already has mc prefix
975 tid = fn + ":" + task
976 self.target_tids.append(tid)
977 if tid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500978 import difflib
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600979 tasks = []
980 for x in taskData[mc].taskentries:
981 if x.startswith(fn + ":"):
982 tasks.append(taskname_from_tid(x))
983 close_matches = difflib.get_close_matches(task, tasks, cutoff=0.7)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500984 if close_matches:
985 extra = ". Close matches:\n %s" % "\n ".join(close_matches)
986 else:
987 extra = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600988 bb.msg.fatal("RunQueue", "Task %s does not exist for target %s (%s)%s" % (task, target, tid, extra))
989
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500990 # For tasks called "XXXX-", ony run their dependencies
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500991 if parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600992 for i in self.runtaskentries[tid].depends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500993 mark_active(i, 1)
994 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600995 mark_active(tid, 1)
996
997 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600998 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500999
1000 # Step C - Prune all inactive tasks
1001 #
1002 # Once all active tasks are marked, prune the ones we don't need.
1003
Brad Bishop316dfdd2018-06-25 12:45:53 -04001004 # Handle --runall
1005 if self.cooker.configuration.runall:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001006 # re-run the mark_active and then drop unused tasks from new list
Andrew Geissler595f6302022-01-24 19:11:47 +00001007 reduced_tasklist = set(self.runtaskentries.keys())
1008 for tid in list(self.runtaskentries.keys()):
1009 if tid not in runq_build:
1010 reduced_tasklist.remove(tid)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001011 runq_build = {}
Brad Bishop316dfdd2018-06-25 12:45:53 -04001012
1013 for task in self.cooker.configuration.runall:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001014 if not task.startswith("do_"):
1015 task = "do_{0}".format(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001016 runall_tids = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00001017 for tid in reduced_tasklist:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001018 wanttid = "{0}:{1}".format(fn_from_tid(tid), task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001019 if wanttid in self.runtaskentries:
1020 runall_tids.add(wanttid)
1021
1022 for tid in list(runall_tids):
Andrew Geissler595f6302022-01-24 19:11:47 +00001023 mark_active(tid, 1)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001024 self.target_tids.append(tid)
Brad Bishop79641f22019-09-10 07:20:22 -04001025 if self.cooker.configuration.force:
1026 invalidate_task(tid, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001027
Andrew Geissler595f6302022-01-24 19:11:47 +00001028 delcount = set()
1029 for tid in list(self.runtaskentries.keys()):
1030 if tid not in runq_build:
1031 delcount.add(tid)
1032 del self.runtaskentries[tid]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001033
Andrew Geissler595f6302022-01-24 19:11:47 +00001034 if self.cooker.configuration.runall:
1035 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001036 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)))
1037
1038 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001039 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001040
1041 # Handle runonly
1042 if self.cooker.configuration.runonly:
1043 # re-run the mark_active and then drop unused tasks from new list
1044 runq_build = {}
1045
1046 for task in self.cooker.configuration.runonly:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001047 if not task.startswith("do_"):
1048 task = "do_{0}".format(task)
Andrew Geissler595f6302022-01-24 19:11:47 +00001049 runonly_tids = [k for k in self.runtaskentries.keys() if taskname_from_tid(k) == task]
Brad Bishop316dfdd2018-06-25 12:45:53 -04001050
Andrew Geissler595f6302022-01-24 19:11:47 +00001051 for tid in runonly_tids:
1052 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001053 if self.cooker.configuration.force:
1054 invalidate_task(tid, False)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001055
1056 for tid in list(self.runtaskentries.keys()):
1057 if tid not in runq_build:
Andrew Geissler595f6302022-01-24 19:11:47 +00001058 delcount.add(tid)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001059 del self.runtaskentries[tid]
1060
Andrew Geissler595f6302022-01-24 19:11:47 +00001061 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001062 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 -05001063
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001064 #
1065 # Step D - Sanity checks and computation
1066 #
1067
1068 # Check to make sure we still have tasks to run
Andrew Geissler595f6302022-01-24 19:11:47 +00001069 if not self.runtaskentries:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001070 if not taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001071 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.")
1072 else:
1073 bb.msg.fatal("RunQueue", "No active tasks and not in --continue mode?! Please report this bug.")
1074
Brad Bishop316dfdd2018-06-25 12:45:53 -04001075 logger.verbose("Pruned %s inactive tasks, %s left", len(delcount), len(self.runtaskentries))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001076
1077 logger.verbose("Assign Weightings")
1078
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001079 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001080 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001081
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001082 # Generate a list of reverse dependencies to ease future calculations
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001083 for tid in self.runtaskentries:
1084 for dep in self.runtaskentries[tid].depends:
1085 self.runtaskentries[dep].revdeps.add(tid)
1086
1087 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001088 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001089
1090 # Identify tasks at the end of dependency chains
1091 # Error on circular dependency loops (length two)
1092 endpoints = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001093 for tid in self.runtaskentries:
1094 revdeps = self.runtaskentries[tid].revdeps
Andrew Geissler595f6302022-01-24 19:11:47 +00001095 if not revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001096 endpoints.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001097 for dep in revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001098 if dep in self.runtaskentries[tid].depends:
1099 bb.msg.fatal("RunQueue", "Task %s has circular dependency on %s" % (tid, dep))
1100
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001101
1102 logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
1103
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001104 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001105 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001106
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001107 # Calculate task weights
1108 # Check of higher length circular dependencies
1109 self.runq_weight = self.calculate_task_weights(endpoints)
1110
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001111 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001112 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001113
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001114 # Sanity Check - Check for multiple tasks building the same provider
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001115 for mc in self.dataCaches:
1116 prov_list = {}
1117 seen_fn = []
1118 for tid in self.runtaskentries:
1119 (tidmc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1120 if taskfn in seen_fn:
1121 continue
1122 if mc != tidmc:
1123 continue
1124 seen_fn.append(taskfn)
1125 for prov in self.dataCaches[mc].fn_provides[taskfn]:
1126 if prov not in prov_list:
1127 prov_list[prov] = [taskfn]
1128 elif taskfn not in prov_list[prov]:
1129 prov_list[prov].append(taskfn)
1130 for prov in prov_list:
1131 if len(prov_list[prov]) < 2:
1132 continue
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001133 if prov in self.multi_provider_allowed:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001134 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001135 seen_pn = []
1136 # If two versions of the same PN are being built its fatal, we don't support it.
1137 for fn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001138 pn = self.dataCaches[mc].pkg_fn[fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001139 if pn not in seen_pn:
1140 seen_pn.append(pn)
1141 else:
1142 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 +00001143 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 -05001144 #
1145 # Construct a list of things which uniquely depend on each provider
1146 # since this may help the user figure out which dependency is triggering this warning
1147 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001148 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 -05001149 deplist = {}
1150 commondeps = None
1151 for provfn in prov_list[prov]:
1152 deps = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001153 for tid in self.runtaskentries:
1154 fn = fn_from_tid(tid)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001155 if fn != provfn:
1156 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001157 for dep in self.runtaskentries[tid].revdeps:
1158 fn = fn_from_tid(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001159 if fn == provfn:
1160 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001161 deps.add(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001162 if not commondeps:
1163 commondeps = set(deps)
1164 else:
1165 commondeps &= deps
1166 deplist[provfn] = deps
1167 for provfn in deplist:
Andrew Geissler595f6302022-01-24 19:11:47 +00001168 msgs.append("\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001169 #
1170 # Construct a list of provides and runtime providers for each recipe
1171 # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC)
1172 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001173 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 -05001174 provide_results = {}
1175 rprovide_results = {}
1176 commonprovs = None
1177 commonrprovs = None
1178 for provfn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001179 provides = set(self.dataCaches[mc].fn_provides[provfn])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001180 rprovides = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001181 for rprovide in self.dataCaches[mc].rproviders:
1182 if provfn in self.dataCaches[mc].rproviders[rprovide]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001183 rprovides.add(rprovide)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001184 for package in self.dataCaches[mc].packages:
1185 if provfn in self.dataCaches[mc].packages[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001186 rprovides.add(package)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001187 for package in self.dataCaches[mc].packages_dynamic:
1188 if provfn in self.dataCaches[mc].packages_dynamic[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001189 rprovides.add(package)
1190 if not commonprovs:
1191 commonprovs = set(provides)
1192 else:
1193 commonprovs &= provides
1194 provide_results[provfn] = provides
1195 if not commonrprovs:
1196 commonrprovs = set(rprovides)
1197 else:
1198 commonrprovs &= rprovides
1199 rprovide_results[provfn] = rprovides
Andrew Geissler595f6302022-01-24 19:11:47 +00001200 #msgs.append("\nCommon provides:\n %s" % ("\n ".join(commonprovs)))
1201 #msgs.append("\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001202 for provfn in prov_list[prov]:
Andrew Geissler595f6302022-01-24 19:11:47 +00001203 msgs.append("\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs)))
1204 msgs.append("\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001205
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001206 if self.warn_multi_bb:
Andrew Geissler595f6302022-01-24 19:11:47 +00001207 logger.verbnote("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001208 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00001209 logger.error("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001210
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001211 self.init_progress_reporter.next_stage()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001212 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001213 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001214
1215 # Iterate over the task list looking for tasks with a 'setscene' function
Andrew Geissler82c905d2020-04-13 13:39:40 -05001216 self.runq_setscene_tids = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001217 if not self.cooker.configuration.nosetscene:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001218 for tid in self.runtaskentries:
1219 (mc, fn, taskname, _) = split_tid_mcfn(tid)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001220 setscenetid = tid + "_setscene"
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001221 if setscenetid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001222 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -05001223 self.runq_setscene_tids.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001224
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001225 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001226 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001227
1228 # Invalidate task if force mode active
1229 if self.cooker.configuration.force:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001230 for tid in self.target_tids:
1231 invalidate_task(tid, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001232
1233 # Invalidate task if invalidate mode active
1234 if self.cooker.configuration.invalidate_stamp:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001235 for tid in self.target_tids:
1236 fn = fn_from_tid(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001237 for st in self.cooker.configuration.invalidate_stamp.split(','):
1238 if not st.startswith("do_"):
1239 st = "do_%s" % st
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001240 invalidate_task(fn + ":" + st, True)
1241
1242 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001243 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001244
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001245 # Create and print to the logs a virtual/xxxx -> PN (fn) table
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001246 for mc in taskData:
1247 virtmap = taskData[mc].get_providermap(prefix="virtual/")
1248 virtpnmap = {}
1249 for v in virtmap:
1250 virtpnmap[v] = self.dataCaches[mc].pkg_fn[virtmap[v]]
1251 bb.debug(2, "%s resolved to: %s (%s)" % (v, virtpnmap[v], virtmap[v]))
1252 if hasattr(bb.parse.siggen, "tasks_resolved"):
1253 bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
1254
1255 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001256 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001257
Brad Bishop00e122a2019-10-05 11:10:57 -04001258 bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
1259
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001260 # Iterate over the task list and call into the siggen code
1261 dealtwith = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001262 todeal = set(self.runtaskentries)
Andrew Geissler595f6302022-01-24 19:11:47 +00001263 while todeal:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001264 for tid in todeal.copy():
Andrew Geissler595f6302022-01-24 19:11:47 +00001265 if not (self.runtaskentries[tid].depends - dealtwith):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001266 dealtwith.add(tid)
1267 todeal.remove(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001268 self.prepare_task_hash(tid)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001269 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001270
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001271 bb.parse.siggen.writeout_file_checksum_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001272
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001273 #self.dump_data()
1274 return len(self.runtaskentries)
1275
Brad Bishop19323692019-04-05 15:28:33 -04001276 def prepare_task_hash(self, tid):
Andrew Geissler517393d2023-01-13 08:55:19 -06001277 bb.parse.siggen.prep_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
1278 self.runtaskentries[tid].hash = bb.parse.siggen.get_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
Brad Bishop08902b02019-08-20 09:16:51 -04001279 self.runtaskentries[tid].unihash = bb.parse.siggen.get_unihash(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001280
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001281 def dump_data(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001282 """
1283 Dump some debug information on the internal data structures
1284 """
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001285 logger.debug3("run_tasks:")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001286 for tid in self.runtaskentries:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001287 logger.debug3(" %s: %s Deps %s RevDeps %s", tid,
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001288 self.runtaskentries[tid].weight,
1289 self.runtaskentries[tid].depends,
1290 self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001291
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001292class RunQueueWorker():
1293 def __init__(self, process, pipe):
1294 self.process = process
1295 self.pipe = pipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001296
1297class RunQueue:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001298 def __init__(self, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001299
1300 self.cooker = cooker
1301 self.cfgData = cfgData
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001302 self.rqdata = RunQueueData(self, cooker, cfgData, dataCaches, taskData, targets)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001303
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001304 self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION") or None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001305 self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID") or None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001306
1307 self.state = runQueuePrepare
1308
1309 # For disk space monitor
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001310 # Invoked at regular time intervals via the bitbake heartbeat event
1311 # while the build is running. We generate a unique name for the handler
1312 # here, just in case that there ever is more than one RunQueue instance,
Brad Bishop96ff1982019-08-19 13:50:42 -04001313 # start the handler when reaching runQueueSceneInit, and stop it when
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001314 # done with the build.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001315 self.dm = monitordisk.diskMonitor(cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001316 self.dm_event_handler_name = '_bb_diskmonitor_' + str(id(self))
1317 self.dm_event_handler_registered = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001318 self.rqexe = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001319 self.worker = {}
1320 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001321
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001322 @staticmethod
1323 def send_pickled_data(worker, data, name):
1324 msg = bytearray()
1325 msg.extend(b"<" + name.encode() + b">")
1326 pickled_data = pickle.dumps(data)
1327 msg.extend(len(pickled_data).to_bytes(4, 'big'))
1328 msg.extend(pickled_data)
1329 msg.extend(b"</" + name.encode() + b">")
1330 worker.stdin.write(msg)
1331
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001332 def _start_worker(self, mc, fakeroot = False, rqexec = None):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001333 logger.debug("Starting bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334 magic = "decafbad"
1335 if self.cooker.configuration.profile:
1336 magic = "decafbadbad"
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001337 fakerootlogs = None
Andrew Geissler220dafd2023-10-04 10:18:08 -05001338
1339 workerscript = os.path.realpath(os.path.dirname(__file__) + "/../../bin/bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001340 if fakeroot:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001341 magic = magic + "beef"
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001342 mcdata = self.cooker.databuilder.mcdata[mc]
Brad Bishop19323692019-04-05 15:28:33 -04001343 fakerootcmd = shlex.split(mcdata.getVar("FAKEROOTCMD"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001344 fakerootenv = (mcdata.getVar("FAKEROOTBASEENV") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001345 env = os.environ.copy()
1346 for key, value in (var.split('=') for var in fakerootenv):
1347 env[key] = value
Andrew Geissler220dafd2023-10-04 10:18:08 -05001348 worker = subprocess.Popen(fakerootcmd + [sys.executable, workerscript, magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001349 fakerootlogs = self.rqdata.dataCaches[mc].fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001350 else:
Andrew Geissler220dafd2023-10-04 10:18:08 -05001351 worker = subprocess.Popen([sys.executable, workerscript, magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001352 bb.utils.nonblockingfd(worker.stdout)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001353 workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, self, rqexec, fakerootlogs=fakerootlogs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001354
1355 workerdata = {
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001356 "sigdata" : bb.parse.siggen.get_taskdata(),
Andrew Geissler82c905d2020-04-13 13:39:40 -05001357 "logdefaultlevel" : bb.msg.loggerDefaultLogLevel,
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001358 "build_verbose_shell" : self.cooker.configuration.build_verbose_shell,
1359 "build_verbose_stdout" : self.cooker.configuration.build_verbose_stdout,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001360 "logdefaultdomain" : bb.msg.loggerDefaultDomains,
1361 "prhost" : self.cooker.prhost,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001362 "buildname" : self.cfgData.getVar("BUILDNAME"),
1363 "date" : self.cfgData.getVar("DATE"),
1364 "time" : self.cfgData.getVar("TIME"),
Brad Bishopa34c0302019-09-23 22:34:48 -04001365 "hashservaddr" : self.cooker.hashservaddr,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001366 "umask" : self.cfgData.getVar("BB_DEFAULT_UMASK"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001367 }
1368
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001369 RunQueue.send_pickled_data(worker, self.cooker.configuration, "cookerconfig")
1370 RunQueue.send_pickled_data(worker, self.cooker.extraconfigdata, "extraconfigdata")
1371 RunQueue.send_pickled_data(worker, workerdata, "workerdata")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001372 worker.stdin.flush()
1373
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001374 return RunQueueWorker(worker, workerpipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001375
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001376 def _teardown_worker(self, worker):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001377 if not worker:
1378 return
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001379 logger.debug("Teardown for bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001380 try:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001381 RunQueue.send_pickled_data(worker.process, b"", "quit")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001382 worker.process.stdin.flush()
1383 worker.process.stdin.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001384 except IOError:
1385 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001386 while worker.process.returncode is None:
1387 worker.pipe.read()
1388 worker.process.poll()
1389 while worker.pipe.read():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001390 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001391 worker.pipe.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001392
1393 def start_worker(self):
1394 if self.worker:
1395 self.teardown_workers()
1396 self.teardown = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001397 for mc in self.rqdata.dataCaches:
1398 self.worker[mc] = self._start_worker(mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001399
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001400 def start_fakeworker(self, rqexec, mc):
1401 if not mc in self.fakeworker:
1402 self.fakeworker[mc] = self._start_worker(mc, True, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001403
1404 def teardown_workers(self):
1405 self.teardown = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001406 for mc in self.worker:
1407 self._teardown_worker(self.worker[mc])
1408 self.worker = {}
1409 for mc in self.fakeworker:
1410 self._teardown_worker(self.fakeworker[mc])
1411 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001412
1413 def read_workers(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001414 for mc in self.worker:
1415 self.worker[mc].pipe.read()
1416 for mc in self.fakeworker:
1417 self.fakeworker[mc].pipe.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001418
1419 def active_fds(self):
1420 fds = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001421 for mc in self.worker:
1422 fds.append(self.worker[mc].pipe.input)
1423 for mc in self.fakeworker:
1424 fds.append(self.fakeworker[mc].pipe.input)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001425 return fds
1426
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001427 def check_stamp_task(self, tid, taskname = None, recurse = False, cache = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001428 def get_timestamp(f):
1429 try:
1430 if not os.access(f, os.F_OK):
1431 return None
1432 return os.stat(f)[stat.ST_MTIME]
1433 except:
1434 return None
1435
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001436 (mc, fn, tn, taskfn) = split_tid_mcfn(tid)
1437 if taskname is None:
1438 taskname = tn
1439
Andrew Geissler517393d2023-01-13 08:55:19 -06001440 stampfile = bb.parse.siggen.stampfile_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001441
1442 # If the stamp is missing, it's not current
1443 if not os.access(stampfile, os.F_OK):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001444 logger.debug2("Stampfile %s not available", stampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001445 return False
1446 # If it's a 'nostamp' task, it's not current
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001447 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001448 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001449 logger.debug2("%s.%s is nostamp\n", fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001450 return False
1451
Andrew Geissler517393d2023-01-13 08:55:19 -06001452 if taskname.endswith("_setscene"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001453 return True
1454
1455 if cache is None:
1456 cache = {}
1457
1458 iscurrent = True
1459 t1 = get_timestamp(stampfile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001460 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001461 if iscurrent:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001462 (mc2, fn2, taskname2, taskfn2) = split_tid_mcfn(dep)
Andrew Geissler517393d2023-01-13 08:55:19 -06001463 stampfile2 = bb.parse.siggen.stampfile_mcfn(taskname2, taskfn2)
1464 stampfile3 = bb.parse.siggen.stampfile_mcfn(taskname2 + "_setscene", taskfn2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001465 t2 = get_timestamp(stampfile2)
1466 t3 = get_timestamp(stampfile3)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001467 if t3 and not t2:
1468 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001469 if t3 and t3 > t2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001470 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00001471 if fn == fn2:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001472 if not t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001473 logger.debug2('Stampfile %s does not exist', stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001474 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001475 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001476 if t1 < t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001477 logger.debug2('Stampfile %s < %s', stampfile, stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001478 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001479 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001480 if recurse and iscurrent:
1481 if dep in cache:
1482 iscurrent = cache[dep]
1483 if not iscurrent:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001484 logger.debug2('Stampfile for dependency %s:%s invalid (cached)' % (fn2, taskname2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001485 else:
1486 iscurrent = self.check_stamp_task(dep, recurse=True, cache=cache)
1487 cache[dep] = iscurrent
1488 if recurse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001489 cache[tid] = iscurrent
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001490 return iscurrent
1491
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001492 def validate_hashes(self, tocheck, data, currentcount=0, siginfo=False, summary=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04001493 valid = set()
1494 if self.hashvalidate:
Brad Bishop08902b02019-08-20 09:16:51 -04001495 sq_data = {}
1496 sq_data['hash'] = {}
1497 sq_data['hashfn'] = {}
1498 sq_data['unihash'] = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04001499 for tid in tocheck:
1500 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04001501 sq_data['hash'][tid] = self.rqdata.runtaskentries[tid].hash
1502 sq_data['hashfn'][tid] = self.rqdata.dataCaches[mc].hashfn[taskfn]
1503 sq_data['unihash'][tid] = self.rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -04001504
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001505 valid = self.validate_hash(sq_data, data, siginfo, currentcount, summary)
Brad Bishop96ff1982019-08-19 13:50:42 -04001506
1507 return valid
1508
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001509 def validate_hash(self, sq_data, d, siginfo, currentcount, summary):
1510 locs = {"sq_data" : sq_data, "d" : d, "siginfo" : siginfo, "currentcount" : currentcount, "summary" : summary}
Brad Bishop19323692019-04-05 15:28:33 -04001511
Brad Bishop08902b02019-08-20 09:16:51 -04001512 # Metadata has **kwargs so args can be added, sq_data can also gain new fields
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001513 call = self.hashvalidate + "(sq_data, d, siginfo=siginfo, currentcount=currentcount, summary=summary)"
Brad Bishop19323692019-04-05 15:28:33 -04001514
Brad Bishop19323692019-04-05 15:28:33 -04001515 return bb.utils.better_eval(call, locs)
1516
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001517 def _execute_runqueue(self):
1518 """
1519 Run the tasks in a queue prepared by rqdata.prepare()
1520 Upon failure, optionally try to recover the build using any alternate providers
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001521 (if the halt on failure configuration option isn't set)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001522 """
1523
1524 retval = True
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001525 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001526
1527 if self.state is runQueuePrepare:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001528 # NOTE: if you add, remove or significantly refactor the stages of this
1529 # process then you should recalculate the weightings here. This is quite
1530 # easy to do - just change the next line temporarily to pass debug=True as
1531 # the last parameter and you'll get a printout of the weightings as well
1532 # as a map to the lines where next_stage() was called. Of course this isn't
1533 # critical, but it helps to keep the progress reporting accurate.
1534 self.rqdata.init_progress_reporter = bb.progress.MultiStageProcessProgressReporter(self.cooker.data,
1535 "Initialising tasks",
1536 [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 -05001537 if self.rqdata.prepare() == 0:
1538 self.state = runQueueComplete
1539 else:
1540 self.state = runQueueSceneInit
Brad Bishop00e122a2019-10-05 11:10:57 -04001541 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001542
1543 if self.state is runQueueSceneInit:
Brad Bishop96ff1982019-08-19 13:50:42 -04001544 self.rqdata.init_progress_reporter.next_stage()
1545
1546 # we are ready to run, emit dependency info to any UI or class which
1547 # needs it
1548 depgraph = self.cooker.buildDependTree(self, self.rqdata.taskData)
1549 self.rqdata.init_progress_reporter.next_stage()
1550 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
1551
Brad Bishope2d5b612018-11-23 10:55:50 +13001552 if not self.dm_event_handler_registered:
1553 res = bb.event.register(self.dm_event_handler_name,
Andrew Geissler517393d2023-01-13 08:55:19 -06001554 lambda x, y: self.dm.check(self) if self.state in [runQueueRunning, runQueueCleanUp] else False,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001555 ('bb.event.HeartbeatEvent',), data=self.cfgData)
Brad Bishope2d5b612018-11-23 10:55:50 +13001556 self.dm_event_handler_registered = True
1557
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001558 dump = self.cooker.configuration.dump_signatures
1559 if dump:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001560 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001561 if 'printdiff' in dump:
1562 invalidtasks = self.print_diffscenetasks()
1563 self.dump_signatures(dump)
1564 if 'printdiff' in dump:
1565 self.write_diffscenetasks(invalidtasks)
1566 self.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001567
Brad Bishop96ff1982019-08-19 13:50:42 -04001568 if self.state is runQueueSceneInit:
1569 self.rqdata.init_progress_reporter.next_stage()
1570 self.start_worker()
1571 self.rqdata.init_progress_reporter.next_stage()
1572 self.rqexe = RunQueueExecute(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001573
Brad Bishop96ff1982019-08-19 13:50:42 -04001574 # If we don't have any setscene functions, skip execution
Andrew Geissler595f6302022-01-24 19:11:47 +00001575 if not self.rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04001576 logger.info('No setscene tasks')
1577 for tid in self.rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00001578 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04001579 self.rqexe.setbuildable(tid)
1580 self.rqexe.tasks_notcovered.add(tid)
1581 self.rqexe.sqdone = True
1582 logger.info('Executing Tasks')
1583 self.state = runQueueRunning
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001584
1585 if self.state is runQueueRunning:
1586 retval = self.rqexe.execute()
1587
1588 if self.state is runQueueCleanUp:
1589 retval = self.rqexe.finish()
1590
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001591 build_done = self.state is runQueueComplete or self.state is runQueueFailed
1592
1593 if build_done and self.dm_event_handler_registered:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001594 bb.event.remove(self.dm_event_handler_name, None, data=self.cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001595 self.dm_event_handler_registered = False
1596
1597 if build_done and self.rqexe:
Brad Bishop08902b02019-08-20 09:16:51 -04001598 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001599 self.teardown_workers()
Brad Bishop96ff1982019-08-19 13:50:42 -04001600 if self.rqexe:
1601 if self.rqexe.stats.failed:
1602 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)
1603 else:
1604 # Let's avoid the word "failed" if nothing actually did
1605 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 -05001606
1607 if self.state is runQueueFailed:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001608 raise bb.runqueue.TaskFailure(self.rqexe.failed_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001609
1610 if self.state is runQueueComplete:
1611 # All done
1612 return False
1613
1614 # Loop
1615 return retval
1616
1617 def execute_runqueue(self):
1618 # Catch unexpected exceptions and ensure we exit when an error occurs, not loop.
1619 try:
1620 return self._execute_runqueue()
1621 except bb.runqueue.TaskFailure:
1622 raise
1623 except SystemExit:
1624 raise
1625 except bb.BBHandledException:
1626 try:
1627 self.teardown_workers()
1628 except:
1629 pass
1630 self.state = runQueueComplete
1631 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001632 except Exception as err:
1633 logger.exception("An uncaught exception occurred in runqueue")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001634 try:
1635 self.teardown_workers()
1636 except:
1637 pass
1638 self.state = runQueueComplete
1639 raise
1640
1641 def finish_runqueue(self, now = False):
1642 if not self.rqexe:
1643 self.state = runQueueComplete
1644 return
1645
1646 if now:
1647 self.rqexe.finish_now()
1648 else:
1649 self.rqexe.finish()
1650
Andrew Geissler517393d2023-01-13 08:55:19 -06001651 def _rq_dump_sigtid(self, tids):
1652 for tid in tids:
1653 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1654 dataCaches = self.rqdata.dataCaches
1655 bb.parse.siggen.dump_sigtask(taskfn, taskname, dataCaches[mc].stamp[taskfn], True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001656
1657 def dump_signatures(self, options):
Andrew Geissler517393d2023-01-13 08:55:19 -06001658 if bb.cooker.CookerFeatures.RECIPE_SIGGEN_INFO not in self.cooker.featureset:
1659 bb.fatal("The dump signatures functionality needs the RECIPE_SIGGEN_INFO feature enabled")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001660
Andrew Geissler517393d2023-01-13 08:55:19 -06001661 bb.note("Writing task signature files")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001662
1663 max_process = int(self.cfgData.getVar("BB_NUMBER_PARSE_THREADS") or os.cpu_count() or 1)
Andrew Geissler517393d2023-01-13 08:55:19 -06001664 def chunkify(l, n):
1665 return [l[i::n] for i in range(n)]
1666 tids = chunkify(list(self.rqdata.runtaskentries), max_process)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001667 # We cannot use the real multiprocessing.Pool easily due to some local data
1668 # that can't be pickled. This is a cheap multi-process solution.
1669 launched = []
Andrew Geissler517393d2023-01-13 08:55:19 -06001670 while tids:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001671 if len(launched) < max_process:
Andrew Geissler517393d2023-01-13 08:55:19 -06001672 p = Process(target=self._rq_dump_sigtid, args=(tids.pop(), ))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001673 p.start()
1674 launched.append(p)
1675 for q in launched:
1676 # The finished processes are joined when calling is_alive()
1677 if not q.is_alive():
1678 launched.remove(q)
1679 for p in launched:
1680 p.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001681
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001682 bb.parse.siggen.dump_sigs(self.rqdata.dataCaches, options)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001683
1684 return
1685
1686 def print_diffscenetasks(self):
1687
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001688 noexec = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001689 tocheck = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001690
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001691 for tid in self.rqdata.runtaskentries:
1692 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1693 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001694
1695 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001696 noexec.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001697 continue
1698
Brad Bishop96ff1982019-08-19 13:50:42 -04001699 tocheck.add(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001700
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001701 valid_new = self.validate_hashes(tocheck, self.cooker.data, 0, True, summary=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001702
1703 # Tasks which are both setscene and noexec never care about dependencies
1704 # We therefore find tasks which are setscene and noexec and mark their
1705 # unique dependencies as valid.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001706 for tid in noexec:
1707 if tid not in self.rqdata.runq_setscene_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001708 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001709 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001710 hasnoexecparents = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001711 for dep2 in self.rqdata.runtaskentries[dep].revdeps:
1712 if dep2 in self.rqdata.runq_setscene_tids and dep2 in noexec:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001713 continue
1714 hasnoexecparents = False
1715 break
1716 if hasnoexecparents:
1717 valid_new.add(dep)
1718
1719 invalidtasks = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001720 for tid in self.rqdata.runtaskentries:
1721 if tid not in valid_new and tid not in noexec:
1722 invalidtasks.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001723
1724 found = set()
1725 processed = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001726 for tid in invalidtasks:
1727 toprocess = set([tid])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001728 while toprocess:
1729 next = set()
1730 for t in toprocess:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001731 for dep in self.rqdata.runtaskentries[t].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001732 if dep in invalidtasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001733 found.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001734 if dep not in processed:
1735 processed.add(dep)
1736 next.add(dep)
1737 toprocess = next
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001738 if tid in found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001739 toprocess = set()
1740
1741 tasklist = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001742 for tid in invalidtasks.difference(found):
1743 tasklist.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001744
1745 if tasklist:
1746 bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
1747
1748 return invalidtasks.difference(found)
1749
1750 def write_diffscenetasks(self, invalidtasks):
1751
1752 # Define recursion callback
1753 def recursecb(key, hash1, hash2):
1754 hashes = [hash1, hash2]
1755 hashfiles = bb.siggen.find_siginfo(key, None, hashes, self.cfgData)
1756
1757 recout = []
1758 if len(hashfiles) == 2:
1759 out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
Brad Bishopc342db32019-05-15 21:57:59 -04001760 recout.extend(list(' ' + l for l in out2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001761 else:
1762 recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
1763
1764 return recout
1765
1766
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001767 for tid in invalidtasks:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001768 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1769 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001770 h = self.rqdata.runtaskentries[tid].unihash
Patrick Williams03907ee2022-05-01 06:28:52 -05001771 matches = bb.siggen.find_siginfo(pn, taskname, [], self.cooker.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001772 match = None
1773 for m in matches:
1774 if h in m:
1775 match = m
1776 if match is None:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001777 bb.fatal("Can't find a task we're supposed to have written out? (hash: %s tid: %s)?" % (h, tid))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001778 matches = {k : v for k, v in iter(matches.items()) if h not in k}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001779 if matches:
1780 latestmatch = sorted(matches.keys(), key=lambda f: matches[f])[-1]
Brad Bishop19323692019-04-05 15:28:33 -04001781 prevh = __find_sha256__.search(latestmatch).group(0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001782 output = bb.siggen.compare_sigfiles(latestmatch, match, recursecb)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001783 bb.plain("\nTask %s:%s couldn't be used from the cache because:\n We need hash %s, most recent matching task was %s\n " % (pn, taskname, h, prevh) + '\n '.join(output))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001784
Brad Bishop96ff1982019-08-19 13:50:42 -04001785
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001786class RunQueueExecute:
1787
1788 def __init__(self, rq):
1789 self.rq = rq
1790 self.cooker = rq.cooker
1791 self.cfgData = rq.cfgData
1792 self.rqdata = rq.rqdata
1793
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001794 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
1795 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001796 self.max_cpu_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_CPU")
1797 self.max_io_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_IO")
Patrick Williams92b42cb2022-09-03 06:53:57 -05001798 self.max_memory_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_MEMORY")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001799
Brad Bishop96ff1982019-08-19 13:50:42 -04001800 self.sq_buildable = set()
1801 self.sq_running = set()
1802 self.sq_live = set()
1803
Brad Bishop08902b02019-08-20 09:16:51 -04001804 self.updated_taskhash_queue = []
1805 self.pending_migrations = set()
1806
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001807 self.runq_buildable = set()
1808 self.runq_running = set()
1809 self.runq_complete = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001810 self.runq_tasksrun = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001811
1812 self.build_stamps = {}
1813 self.build_stamps2 = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001814 self.failed_tids = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001815 self.sq_deferred = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001816
1817 self.stampcache = {}
1818
Brad Bishop08902b02019-08-20 09:16:51 -04001819 self.holdoff_tasks = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04001820 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04001821 self.sqdone = False
1822
Andrew Geissler5199d832021-09-24 16:47:35 -05001823 self.stats = RunQueueStats(len(self.rqdata.runtaskentries), len(self.rqdata.runq_setscene_tids))
Brad Bishop96ff1982019-08-19 13:50:42 -04001824
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001825 for mc in rq.worker:
1826 rq.worker[mc].pipe.setrunqueueexec(self)
1827 for mc in rq.fakeworker:
1828 rq.fakeworker[mc].pipe.setrunqueueexec(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001829
1830 if self.number_tasks <= 0:
1831 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks)
1832
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001833 lower_limit = 1.0
1834 upper_limit = 1000000.0
1835 if self.max_cpu_pressure:
1836 self.max_cpu_pressure = float(self.max_cpu_pressure)
1837 if self.max_cpu_pressure < lower_limit:
1838 bb.fatal("Invalid BB_PRESSURE_MAX_CPU %s, minimum value is %s." % (self.max_cpu_pressure, lower_limit))
1839 if self.max_cpu_pressure > upper_limit:
1840 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))
1841
1842 if self.max_io_pressure:
1843 self.max_io_pressure = float(self.max_io_pressure)
1844 if self.max_io_pressure < lower_limit:
1845 bb.fatal("Invalid BB_PRESSURE_MAX_IO %s, minimum value is %s." % (self.max_io_pressure, lower_limit))
1846 if self.max_io_pressure > upper_limit:
1847 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))
1848
Patrick Williams92b42cb2022-09-03 06:53:57 -05001849 if self.max_memory_pressure:
1850 self.max_memory_pressure = float(self.max_memory_pressure)
1851 if self.max_memory_pressure < lower_limit:
1852 bb.fatal("Invalid BB_PRESSURE_MAX_MEMORY %s, minimum value is %s." % (self.max_memory_pressure, lower_limit))
1853 if self.max_memory_pressure > upper_limit:
1854 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))
1855
Brad Bishop96ff1982019-08-19 13:50:42 -04001856 # List of setscene tasks which we've covered
1857 self.scenequeue_covered = set()
1858 # List of tasks which are covered (including setscene ones)
1859 self.tasks_covered = set()
1860 self.tasks_scenequeue_done = set()
1861 self.scenequeue_notcovered = set()
1862 self.tasks_notcovered = set()
1863 self.scenequeue_notneeded = set()
1864
Brad Bishop96ff1982019-08-19 13:50:42 -04001865 schedulers = self.get_schedulers()
1866 for scheduler in schedulers:
1867 if self.scheduler == scheduler.name:
1868 self.sched = scheduler(self, self.rqdata)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001869 logger.debug("Using runqueue scheduler '%s'", scheduler.name)
Brad Bishop96ff1982019-08-19 13:50:42 -04001870 break
1871 else:
1872 bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
1873 (self.scheduler, ", ".join(obj.name for obj in schedulers)))
1874
Andrew Geissler595f6302022-01-24 19:11:47 +00001875 #if self.rqdata.runq_setscene_tids:
Brad Bishop08902b02019-08-20 09:16:51 -04001876 self.sqdata = SQData()
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001877 build_scenequeue_data(self.sqdata, self.rqdata, self)
1878
1879 update_scenequeue_data(self.sqdata.sq_revdeps, self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=True)
1880
1881 # Compute a list of 'stale' sstate tasks where the current hash does not match the one
1882 # in any stamp files. Pass the list out to metadata as an event.
1883 found = {}
1884 for tid in self.rqdata.runq_setscene_tids:
1885 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1886 stamps = bb.build.find_stale_stamps(taskname, taskfn)
1887 if stamps:
1888 if mc not in found:
1889 found[mc] = {}
1890 found[mc][tid] = stamps
1891 for mc in found:
1892 event = bb.event.StaleSetSceneTasks(found[mc])
1893 bb.event.fire(event, self.cooker.databuilder.mcdata[mc])
Brad Bishop96ff1982019-08-19 13:50:42 -04001894
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001895 def runqueue_process_waitpid(self, task, status, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001896
1897 # self.build_stamps[pid] may not exist when use shared work directory.
1898 if task in self.build_stamps:
1899 self.build_stamps2.remove(self.build_stamps[task])
1900 del self.build_stamps[task]
1901
Brad Bishop96ff1982019-08-19 13:50:42 -04001902 if task in self.sq_live:
1903 if status != 0:
1904 self.sq_task_fail(task, status)
1905 else:
1906 self.sq_task_complete(task)
1907 self.sq_live.remove(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05001908 self.stats.updateActiveSetscene(len(self.sq_live))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001909 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04001910 if status != 0:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001911 self.task_fail(task, status, fakerootlog=fakerootlog)
Brad Bishop96ff1982019-08-19 13:50:42 -04001912 else:
1913 self.task_complete(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001914 return True
1915
1916 def finish_now(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001917 for mc in self.rq.worker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001918 try:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001919 RunQueue.send_pickled_data(self.rq.worker[mc].process, b"", "finishnow")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001920 self.rq.worker[mc].process.stdin.flush()
1921 except IOError:
1922 # worker must have died?
1923 pass
1924 for mc in self.rq.fakeworker:
1925 try:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001926 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, b"", "finishnow")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001927 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001928 except IOError:
1929 # worker must have died?
1930 pass
1931
Andrew Geissler595f6302022-01-24 19:11:47 +00001932 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001933 self.rq.state = runQueueFailed
1934 return
1935
1936 self.rq.state = runQueueComplete
1937 return
1938
1939 def finish(self):
1940 self.rq.state = runQueueCleanUp
1941
Andrew Geissler5199d832021-09-24 16:47:35 -05001942 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001943 if active > 0:
1944 bb.event.fire(runQueueExitWait(active), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001945 self.rq.read_workers()
1946 return self.rq.active_fds()
1947
Andrew Geissler595f6302022-01-24 19:11:47 +00001948 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001949 self.rq.state = runQueueFailed
1950 return True
1951
1952 self.rq.state = runQueueComplete
1953 return True
1954
Brad Bishop96ff1982019-08-19 13:50:42 -04001955 # Used by setscene only
1956 def check_dependencies(self, task, taskdeps):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001957 if not self.rq.depvalidate:
1958 return False
1959
Brad Bishop08902b02019-08-20 09:16:51 -04001960 # Must not edit parent data
1961 taskdeps = set(taskdeps)
1962
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001963 taskdata = {}
1964 taskdeps.add(task)
1965 for dep in taskdeps:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001966 (mc, fn, taskname, taskfn) = split_tid_mcfn(dep)
1967 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001968 taskdata[dep] = [pn, taskname, fn]
1969 call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001970 locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001971 valid = bb.utils.better_eval(call, locs)
1972 return valid
1973
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001974 def can_start_task(self):
Andrew Geissler5199d832021-09-24 16:47:35 -05001975 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001976 can_start = active < self.number_tasks
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001977 return can_start
1978
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001979 def get_schedulers(self):
1980 schedulers = set(obj for obj in globals().values()
1981 if type(obj) is type and
1982 issubclass(obj, RunQueueScheduler))
1983
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001984 user_schedulers = self.cfgData.getVar("BB_SCHEDULERS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001985 if user_schedulers:
1986 for sched in user_schedulers.split():
1987 if not "." in sched:
1988 bb.note("Ignoring scheduler '%s' from BB_SCHEDULERS: not an import" % sched)
1989 continue
1990
1991 modname, name = sched.rsplit(".", 1)
1992 try:
1993 module = __import__(modname, fromlist=(name,))
1994 except ImportError as exc:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001995 bb.fatal("Unable to import scheduler '%s' from '%s': %s" % (name, modname, exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001996 else:
1997 schedulers.add(getattr(module, name))
1998 return schedulers
1999
2000 def setbuildable(self, task):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002001 self.runq_buildable.add(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04002002 self.sched.newbuildable(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002003
2004 def task_completeoutright(self, task):
2005 """
2006 Mark a task as completed
2007 Look at the reverse dependencies and mark any task with
2008 completed dependencies as buildable
2009 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002010 self.runq_complete.add(task)
2011 for revdep in self.rqdata.runtaskentries[task].revdeps:
2012 if revdep in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002013 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002014 if revdep in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002015 continue
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002016 alldeps = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002017 for dep in self.rqdata.runtaskentries[revdep].depends:
2018 if dep not in self.runq_complete:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002019 alldeps = False
2020 break
2021 if alldeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002022 self.setbuildable(revdep)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002023 logger.debug("Marking task %s as buildable", revdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002024
Andrew Geissler8f840682023-07-21 09:09:43 -05002025 found = None
2026 for t in sorted(self.sq_deferred.copy()):
Andrew Geissler5199d832021-09-24 16:47:35 -05002027 if self.sq_deferred[t] == task:
Andrew Geissler8f840682023-07-21 09:09:43 -05002028 # Allow the next deferred task to run. Any other deferred tasks should be deferred after that task.
2029 # We shouldn't allow all to run at once as it is prone to races.
2030 if not found:
2031 bb.debug(1, "Deferred task %s now buildable" % t)
2032 del self.sq_deferred[t]
2033 update_scenequeue_data([t], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
2034 found = t
2035 else:
2036 bb.debug(1, "Deferring %s after %s" % (t, found))
2037 self.sq_deferred[t] = found
Andrew Geissler5199d832021-09-24 16:47:35 -05002038
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002039 def task_complete(self, task):
2040 self.stats.taskCompleted()
2041 bb.event.fire(runQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
2042 self.task_completeoutright(task)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002043 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002044
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002045 def task_fail(self, task, exitcode, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002046 """
2047 Called when a task has failed
2048 Updates the state engine with the failure
2049 """
2050 self.stats.taskFailed()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002051 self.failed_tids.append(task)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002052
Andrew Geissler595f6302022-01-24 19:11:47 +00002053 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002054 if fakerootlog and os.path.exists(fakerootlog):
2055 with open(fakerootlog) as fakeroot_log_file:
2056 fakeroot_failed = False
2057 for line in reversed(fakeroot_log_file.readlines()):
2058 for fakeroot_error in ['mismatch', 'error', 'fatal']:
2059 if fakeroot_error in line.lower():
2060 fakeroot_failed = True
2061 if 'doing new pid setup and server start' in line:
2062 break
Andrew Geissler595f6302022-01-24 19:11:47 +00002063 fakeroot_log.append(line)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002064
2065 if not fakeroot_failed:
Andrew Geissler595f6302022-01-24 19:11:47 +00002066 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002067
Andrew Geissler595f6302022-01-24 19:11:47 +00002068 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 -05002069
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002070 if self.rqdata.taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002071 self.rq.state = runQueueCleanUp
2072
2073 def task_skip(self, task, reason):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002074 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002075 self.setbuildable(task)
2076 bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
2077 self.task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002078 self.stats.taskSkipped()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002079 self.stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002080
Brad Bishop08902b02019-08-20 09:16:51 -04002081 def summarise_scenequeue_errors(self):
2082 err = False
2083 if not self.sqdone:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002084 logger.debug('We could skip tasks %s', "\n".join(sorted(self.scenequeue_covered)))
Andrew Geissler5199d832021-09-24 16:47:35 -05002085 completeevent = sceneQueueComplete(self.stats, self.rq)
Brad Bishop08902b02019-08-20 09:16:51 -04002086 bb.event.fire(completeevent, self.cfgData)
2087 if self.sq_deferred:
2088 logger.error("Scenequeue had deferred entries: %s" % pprint.pformat(self.sq_deferred))
2089 err = True
2090 if self.updated_taskhash_queue:
2091 logger.error("Scenequeue had unprocessed changed taskhash entries: %s" % pprint.pformat(self.updated_taskhash_queue))
2092 err = True
2093 if self.holdoff_tasks:
2094 logger.error("Scenequeue had holdoff tasks: %s" % pprint.pformat(self.holdoff_tasks))
2095 err = True
2096
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002097 for tid in self.scenequeue_covered.intersection(self.scenequeue_notcovered):
2098 # No task should end up in both covered and uncovered, that is a bug.
2099 logger.error("Setscene task %s in both covered and notcovered." % tid)
2100
Brad Bishop08902b02019-08-20 09:16:51 -04002101 for tid in self.rqdata.runq_setscene_tids:
2102 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2103 err = True
2104 logger.error("Setscene Task %s was never marked as covered or not covered" % tid)
2105 if tid not in self.sq_buildable:
2106 err = True
2107 logger.error("Setscene Task %s was never marked as buildable" % tid)
2108 if tid not in self.sq_running:
2109 err = True
2110 logger.error("Setscene Task %s was never marked as running" % tid)
2111
2112 for x in self.rqdata.runtaskentries:
2113 if x not in self.tasks_covered and x not in self.tasks_notcovered:
2114 logger.error("Task %s was never moved from the setscene queue" % x)
2115 err = True
2116 if x not in self.tasks_scenequeue_done:
2117 logger.error("Task %s was never processed by the setscene code" % x)
2118 err = True
Andrew Geissler595f6302022-01-24 19:11:47 +00002119 if not self.rqdata.runtaskentries[x].depends and x not in self.runq_buildable:
Brad Bishop08902b02019-08-20 09:16:51 -04002120 logger.error("Task %s was never marked as buildable by the setscene code" % x)
2121 err = True
2122 return err
2123
2124
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002125 def execute(self):
2126 """
Brad Bishop96ff1982019-08-19 13:50:42 -04002127 Run the tasks in a queue prepared by prepare_runqueue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002128 """
2129
2130 self.rq.read_workers()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002131 if self.updated_taskhash_queue or self.pending_migrations:
2132 self.process_possible_migrations()
2133
2134 if not hasattr(self, "sorted_setscene_tids"):
2135 # Don't want to sort this set every execution
2136 self.sorted_setscene_tids = sorted(self.rqdata.runq_setscene_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002137
Brad Bishop96ff1982019-08-19 13:50:42 -04002138 task = None
2139 if not self.sqdone and self.can_start_task():
2140 # Find the next setscene to run
Andrew Geissler82c905d2020-04-13 13:39:40 -05002141 for nexttask in self.sorted_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002142 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 +00002143 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 -04002144 if nexttask not in self.rqdata.target_tids:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002145 logger.debug2("Skipping setscene for task %s" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002146 self.sq_task_skip(nexttask)
2147 self.scenequeue_notneeded.add(nexttask)
2148 if nexttask in self.sq_deferred:
2149 del self.sq_deferred[nexttask]
2150 return True
Brad Bishop08902b02019-08-20 09:16:51 -04002151 # If covered tasks are running, need to wait for them to complete
2152 for t in self.sqdata.sq_covered_tasks[nexttask]:
2153 if t in self.runq_running and t not in self.runq_complete:
2154 continue
Brad Bishop96ff1982019-08-19 13:50:42 -04002155 if nexttask in self.sq_deferred:
2156 if self.sq_deferred[nexttask] not in self.runq_complete:
2157 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002158 logger.debug("Task %s no longer deferred" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002159 del self.sq_deferred[nexttask]
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002160 valid = self.rq.validate_hashes(set([nexttask]), self.cooker.data, 0, False, summary=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002161 if not valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002162 logger.debug("%s didn't become valid, skipping setscene" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002163 self.sq_task_failoutright(nexttask)
2164 return True
Brad Bishop96ff1982019-08-19 13:50:42 -04002165 if nexttask in self.sqdata.outrightfail:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002166 logger.debug2('No package found, so skipping setscene task %s', nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002167 self.sq_task_failoutright(nexttask)
2168 return True
2169 if nexttask in self.sqdata.unskippable:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002170 logger.debug2("Setscene task %s is unskippable" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002171 task = nexttask
2172 break
2173 if task is not None:
2174 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
2175 taskname = taskname + "_setscene"
2176 if self.rq.check_stamp_task(task, taskname_from_tid(task), recurse = True, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002177 logger.debug2('Stamp for underlying task %s is current, so skipping setscene variant', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002178 self.sq_task_failoutright(task)
2179 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002180
Brad Bishop96ff1982019-08-19 13:50:42 -04002181 if self.cooker.configuration.force:
2182 if task in self.rqdata.target_tids:
2183 self.sq_task_failoutright(task)
2184 return True
2185
2186 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002187 logger.debug2('Setscene stamp current task %s, so skip it and its dependencies', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002188 self.sq_task_skip(task)
2189 return True
2190
2191 if self.cooker.configuration.skipsetscene:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002192 logger.debug2('No setscene tasks should be executed. Skipping %s', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002193 self.sq_task_failoutright(task)
2194 return True
2195
Andrew Geissler5199d832021-09-24 16:47:35 -05002196 startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
Brad Bishop96ff1982019-08-19 13:50:42 -04002197 bb.event.fire(startevent, self.cfgData)
2198
Brad Bishop96ff1982019-08-19 13:50:42 -04002199 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002200 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002201 runtask = {
2202 'fn' : taskfn,
2203 'task' : task,
2204 'taskname' : taskname,
2205 'taskhash' : self.rqdata.get_task_hash(task),
2206 'unihash' : self.rqdata.get_task_unihash(task),
2207 'quieterrors' : True,
2208 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002209 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002210 'taskdepdata' : self.sq_build_taskdepdata(task),
2211 'dry_run' : False,
2212 'taskdep': taskdep,
2213 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2214 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2215 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2216 }
2217
Brad Bishop96ff1982019-08-19 13:50:42 -04002218 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
2219 if not mc in self.rq.fakeworker:
2220 self.rq.start_fakeworker(self, mc)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002221 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, runtask, "runtask")
Brad Bishop96ff1982019-08-19 13:50:42 -04002222 self.rq.fakeworker[mc].process.stdin.flush()
2223 else:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002224 RunQueue.send_pickled_data(self.rq.worker[mc].process, runtask, "runtask")
Brad Bishop96ff1982019-08-19 13:50:42 -04002225 self.rq.worker[mc].process.stdin.flush()
2226
Andrew Geissler517393d2023-01-13 08:55:19 -06002227 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002228 self.build_stamps2.append(self.build_stamps[task])
2229 self.sq_running.add(task)
2230 self.sq_live.add(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05002231 self.stats.updateActiveSetscene(len(self.sq_live))
Brad Bishop96ff1982019-08-19 13:50:42 -04002232 if self.can_start_task():
2233 return True
2234
Brad Bishopc68388fc2019-08-26 01:33:31 -04002235 self.update_holdofftasks()
2236
Brad Bishop08902b02019-08-20 09:16:51 -04002237 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 -05002238 hashequiv_logger.verbose("Setscene tasks completed")
Brad Bishop96ff1982019-08-19 13:50:42 -04002239
Brad Bishop08902b02019-08-20 09:16:51 -04002240 err = self.summarise_scenequeue_errors()
Brad Bishop96ff1982019-08-19 13:50:42 -04002241 if err:
2242 self.rq.state = runQueueFailed
2243 return True
2244
2245 if self.cooker.configuration.setsceneonly:
2246 self.rq.state = runQueueComplete
2247 return True
2248 self.sqdone = True
2249
2250 if self.stats.total == 0:
2251 # nothing to do
2252 self.rq.state = runQueueComplete
2253 return True
2254
2255 if self.cooker.configuration.setsceneonly:
2256 task = None
2257 else:
2258 task = self.sched.next()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002259 if task is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002260 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002261
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002262 if self.rqdata.setscene_ignore_tasks is not None:
2263 if self.check_setscene_ignore_tasks(task):
2264 self.task_fail(task, "setscene ignore_tasks")
Brad Bishop96ff1982019-08-19 13:50:42 -04002265 return True
2266
2267 if task in self.tasks_covered:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002268 logger.debug2("Setscene covered task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002269 self.task_skip(task, "covered")
2270 return True
2271
2272 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002273 logger.debug2("Stamp current task %s", task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002274
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002275 self.task_skip(task, "existing")
Andrew Geissler82c905d2020-04-13 13:39:40 -05002276 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002277 return True
2278
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002279 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002280 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2281 startevent = runQueueTaskStarted(task, self.stats, self.rq,
2282 noexec=True)
2283 bb.event.fire(startevent, self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002284 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002285 self.stats.taskActive()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002286 if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Andrew Geissler517393d2023-01-13 08:55:19 -06002287 bb.build.make_stamp_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002288 self.task_complete(task)
2289 return True
2290 else:
2291 startevent = runQueueTaskStarted(task, self.stats, self.rq)
2292 bb.event.fire(startevent, self.cfgData)
2293
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002294 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002295 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002296 runtask = {
2297 'fn' : taskfn,
2298 'task' : task,
2299 'taskname' : taskname,
2300 'taskhash' : self.rqdata.get_task_hash(task),
2301 'unihash' : self.rqdata.get_task_unihash(task),
2302 'quieterrors' : False,
2303 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002304 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002305 'taskdepdata' : self.build_taskdepdata(task),
2306 'dry_run' : self.rqdata.setscene_enforce,
2307 'taskdep': taskdep,
2308 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2309 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2310 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2311 }
2312
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002313 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 -05002314 if not mc in self.rq.fakeworker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002315 try:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002316 self.rq.start_fakeworker(self, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002317 except OSError as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002318 logger.critical("Failed to spawn fakeroot worker to run %s: %s" % (task, str(exc)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002319 self.rq.state = runQueueFailed
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002320 self.stats.taskFailed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002321 return True
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002322 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, runtask, "runtask")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002323 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002324 else:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002325 RunQueue.send_pickled_data(self.rq.worker[mc].process, runtask, "runtask")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002326 self.rq.worker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002327
Andrew Geissler517393d2023-01-13 08:55:19 -06002328 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002329 self.build_stamps2.append(self.build_stamps[task])
2330 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002331 self.stats.taskActive()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002332 if self.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002333 return True
2334
Andrew Geissler595f6302022-01-24 19:11:47 +00002335 if self.stats.active > 0 or self.sq_live:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002336 self.rq.read_workers()
2337 return self.rq.active_fds()
2338
Brad Bishop96ff1982019-08-19 13:50:42 -04002339 # No more tasks can be run. If we have deferred setscene tasks we should run them.
2340 if self.sq_deferred:
Patrick Williams92b42cb2022-09-03 06:53:57 -05002341 deferred_tid = list(self.sq_deferred.keys())[0]
2342 blocking_tid = self.sq_deferred.pop(deferred_tid)
Patrick Williams2390b1b2022-11-03 13:47:49 -05002343 logger.warning("Runqueue deadlocked on deferred tasks, forcing task %s blocked by %s" % (deferred_tid, blocking_tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04002344 return True
2345
Andrew Geissler595f6302022-01-24 19:11:47 +00002346 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002347 self.rq.state = runQueueFailed
2348 return True
2349
2350 # Sanity Checks
Brad Bishop08902b02019-08-20 09:16:51 -04002351 err = self.summarise_scenequeue_errors()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002352 for task in self.rqdata.runtaskentries:
2353 if task not in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002354 logger.error("Task %s never buildable!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002355 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002356 elif task not in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002357 logger.error("Task %s never ran!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002358 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002359 elif task not in self.runq_complete:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002360 logger.error("Task %s never completed!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002361 err = True
2362
2363 if err:
2364 self.rq.state = runQueueFailed
2365 else:
2366 self.rq.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002367
2368 return True
2369
Brad Bishopc68388fc2019-08-26 01:33:31 -04002370 def filtermcdeps(self, task, mc, deps):
Andrew Geissler99467da2019-02-25 18:54:23 -06002371 ret = set()
Andrew Geissler99467da2019-02-25 18:54:23 -06002372 for dep in deps:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002373 thismc = mc_from_tid(dep)
2374 if thismc != mc:
Andrew Geissler99467da2019-02-25 18:54:23 -06002375 continue
2376 ret.add(dep)
2377 return ret
2378
Brad Bishopa34c0302019-09-23 22:34:48 -04002379 # We filter out multiconfig dependencies from taskdepdata we pass to the tasks
Andrew Geissler99467da2019-02-25 18:54:23 -06002380 # as most code can't handle them
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002381 def build_taskdepdata(self, task):
2382 taskdepdata = {}
Brad Bishopc68388fc2019-08-26 01:33:31 -04002383 mc = mc_from_tid(task)
Brad Bishop08902b02019-08-20 09:16:51 -04002384 next = self.rqdata.runtaskentries[task].depends.copy()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002385 next.add(task)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002386 next = self.filtermcdeps(task, mc, next)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002387 while next:
2388 additional = []
2389 for revdep in next:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002390 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2391 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2392 deps = self.rqdata.runtaskentries[revdep].depends
2393 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002394 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002395 unihash = self.rqdata.runtaskentries[revdep].unihash
Brad Bishopc68388fc2019-08-26 01:33:31 -04002396 deps = self.filtermcdeps(task, mc, deps)
Patrick Williamsb542dec2023-06-09 01:26:37 -05002397 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2398 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002399 for revdep2 in deps:
2400 if revdep2 not in taskdepdata:
2401 additional.append(revdep2)
2402 next = additional
2403
2404 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2405 return taskdepdata
2406
Brad Bishop08902b02019-08-20 09:16:51 -04002407 def update_holdofftasks(self):
Brad Bishopc68388fc2019-08-26 01:33:31 -04002408
2409 if not self.holdoff_need_update:
2410 return
2411
2412 notcovered = set(self.scenequeue_notcovered)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002413 notcovered |= self.sqdata.cantskip
Brad Bishopc68388fc2019-08-26 01:33:31 -04002414 for tid in self.scenequeue_notcovered:
2415 notcovered |= self.sqdata.sq_covered_tasks[tid]
2416 notcovered |= self.sqdata.unskippable.difference(self.rqdata.runq_setscene_tids)
2417 notcovered.intersection_update(self.tasks_scenequeue_done)
2418
2419 covered = set(self.scenequeue_covered)
2420 for tid in self.scenequeue_covered:
2421 covered |= self.sqdata.sq_covered_tasks[tid]
2422 covered.difference_update(notcovered)
2423 covered.intersection_update(self.tasks_scenequeue_done)
2424
2425 for tid in notcovered | covered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002426 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002427 self.setbuildable(tid)
2428 elif self.rqdata.runtaskentries[tid].depends.issubset(self.runq_complete):
2429 self.setbuildable(tid)
2430
2431 self.tasks_covered = covered
2432 self.tasks_notcovered = notcovered
2433
Brad Bishop08902b02019-08-20 09:16:51 -04002434 self.holdoff_tasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002435
Brad Bishop08902b02019-08-20 09:16:51 -04002436 for tid in self.rqdata.runq_setscene_tids:
2437 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2438 self.holdoff_tasks.add(tid)
2439
2440 for tid in self.holdoff_tasks.copy():
2441 for dep in self.sqdata.sq_covered_tasks[tid]:
2442 if dep not in self.runq_complete:
2443 self.holdoff_tasks.add(dep)
2444
Brad Bishopc68388fc2019-08-26 01:33:31 -04002445 self.holdoff_need_update = False
2446
Brad Bishop08902b02019-08-20 09:16:51 -04002447 def process_possible_migrations(self):
2448
2449 changed = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002450 toprocess = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002451 for tid, unihash in self.updated_taskhash_queue.copy():
2452 if tid in self.runq_running and tid not in self.runq_complete:
2453 continue
2454
2455 self.updated_taskhash_queue.remove((tid, unihash))
2456
2457 if unihash != self.rqdata.runtaskentries[tid].unihash:
Andrew Geisslerc926e172021-05-07 16:11:35 -05002458 # Make sure we rehash any other tasks with the same task hash that we're deferred against.
2459 torehash = [tid]
2460 for deftid in self.sq_deferred:
2461 if self.sq_deferred[deftid] == tid:
2462 torehash.append(deftid)
2463 for hashtid in torehash:
2464 hashequiv_logger.verbose("Task %s unihash changed to %s" % (hashtid, unihash))
2465 self.rqdata.runtaskentries[hashtid].unihash = unihash
2466 bb.parse.siggen.set_unihash(hashtid, unihash)
2467 toprocess.add(hashtid)
Andrew Geissler78b72792022-06-14 06:47:25 -05002468 if torehash:
2469 # Need to save after set_unihash above
2470 bb.parse.siggen.save_unitaskhashes()
Brad Bishop08902b02019-08-20 09:16:51 -04002471
Andrew Geissler82c905d2020-04-13 13:39:40 -05002472 # Work out all tasks which depend upon these
2473 total = set()
2474 next = set()
2475 for p in toprocess:
2476 next |= self.rqdata.runtaskentries[p].revdeps
2477 while next:
2478 current = next.copy()
2479 total = total | next
2480 next = set()
2481 for ntid in current:
2482 next |= self.rqdata.runtaskentries[ntid].revdeps
2483 next.difference_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002484
Andrew Geissler82c905d2020-04-13 13:39:40 -05002485 # Now iterate those tasks in dependency order to regenerate their taskhash/unihash
2486 next = set()
2487 for p in total:
Andrew Geissler595f6302022-01-24 19:11:47 +00002488 if not self.rqdata.runtaskentries[p].depends:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002489 next.add(p)
2490 elif self.rqdata.runtaskentries[p].depends.isdisjoint(total):
2491 next.add(p)
2492
2493 # When an item doesn't have dependencies in total, we can process it. Drop items from total when handled
2494 while next:
2495 current = next.copy()
2496 next = set()
2497 for tid in current:
Andrew Geissler595f6302022-01-24 19:11:47 +00002498 if self.rqdata.runtaskentries[p].depends and not self.rqdata.runtaskentries[tid].depends.isdisjoint(total):
Andrew Geissler82c905d2020-04-13 13:39:40 -05002499 continue
2500 orighash = self.rqdata.runtaskentries[tid].hash
Andrew Geissler517393d2023-01-13 08:55:19 -06002501 newhash = bb.parse.siggen.get_taskhash(tid, self.rqdata.runtaskentries[tid].depends, self.rqdata.dataCaches)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002502 origuni = self.rqdata.runtaskentries[tid].unihash
2503 newuni = bb.parse.siggen.get_unihash(tid)
2504 # FIXME, need to check it can come from sstate at all for determinism?
2505 remapped = False
2506 if newuni == origuni:
2507 # Nothing to do, we match, skip code below
2508 remapped = True
2509 elif tid in self.scenequeue_covered or tid in self.sq_live:
2510 # Already ran this setscene task or it running. Report the new taskhash
2511 bb.parse.siggen.report_unihash_equiv(tid, newhash, origuni, newuni, self.rqdata.dataCaches)
2512 hashequiv_logger.verbose("Already covered setscene for %s so ignoring rehash (remap)" % (tid))
2513 remapped = True
2514
2515 if not remapped:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002516 #logger.debug("Task %s hash changes: %s->%s %s->%s" % (tid, orighash, newhash, origuni, newuni))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002517 self.rqdata.runtaskentries[tid].hash = newhash
2518 self.rqdata.runtaskentries[tid].unihash = newuni
2519 changed.add(tid)
2520
2521 next |= self.rqdata.runtaskentries[tid].revdeps
2522 total.remove(tid)
2523 next.intersection_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002524
2525 if changed:
2526 for mc in self.rq.worker:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002527 RunQueue.send_pickled_data(self.rq.worker[mc].process, bb.parse.siggen.get_taskhashes(), "newtaskhashes")
Brad Bishop08902b02019-08-20 09:16:51 -04002528 for mc in self.rq.fakeworker:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002529 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, bb.parse.siggen.get_taskhashes(), "newtaskhashes")
Brad Bishop08902b02019-08-20 09:16:51 -04002530
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002531 hashequiv_logger.debug(pprint.pformat("Tasks changed:\n%s" % (changed)))
Brad Bishop08902b02019-08-20 09:16:51 -04002532
2533 for tid in changed:
2534 if tid not in self.rqdata.runq_setscene_tids:
2535 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002536 if tid not in self.pending_migrations:
2537 self.pending_migrations.add(tid)
2538
Andrew Geissler82c905d2020-04-13 13:39:40 -05002539 update_tasks = []
Brad Bishop08902b02019-08-20 09:16:51 -04002540 for tid in self.pending_migrations.copy():
Andrew Geissler82c905d2020-04-13 13:39:40 -05002541 if tid in self.runq_running or tid in self.sq_live:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002542 # Too late, task already running, not much we can do now
2543 self.pending_migrations.remove(tid)
2544 continue
2545
Brad Bishop08902b02019-08-20 09:16:51 -04002546 valid = True
2547 # Check no tasks this covers are running
2548 for dep in self.sqdata.sq_covered_tasks[tid]:
2549 if dep in self.runq_running and dep not in self.runq_complete:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002550 hashequiv_logger.debug2("Task %s is running which blocks setscene for %s from running" % (dep, tid))
Brad Bishop08902b02019-08-20 09:16:51 -04002551 valid = False
2552 break
2553 if not valid:
2554 continue
2555
2556 self.pending_migrations.remove(tid)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002557 changed = True
Brad Bishop08902b02019-08-20 09:16:51 -04002558
2559 if tid in self.tasks_scenequeue_done:
2560 self.tasks_scenequeue_done.remove(tid)
2561 for dep in self.sqdata.sq_covered_tasks[tid]:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002562 if dep in self.runq_complete and dep not in self.runq_tasksrun:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002563 bb.error("Task %s marked as completed but now needing to rerun? Halting build." % dep)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002564 self.failed_tids.append(tid)
2565 self.rq.state = runQueueCleanUp
2566 return
2567
Brad Bishop08902b02019-08-20 09:16:51 -04002568 if dep not in self.runq_complete:
2569 if dep in self.tasks_scenequeue_done and dep not in self.sqdata.unskippable:
2570 self.tasks_scenequeue_done.remove(dep)
2571
2572 if tid in self.sq_buildable:
2573 self.sq_buildable.remove(tid)
2574 if tid in self.sq_running:
2575 self.sq_running.remove(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002576 if tid in self.sqdata.outrightfail:
2577 self.sqdata.outrightfail.remove(tid)
2578 if tid in self.scenequeue_notcovered:
2579 self.scenequeue_notcovered.remove(tid)
2580 if tid in self.scenequeue_covered:
2581 self.scenequeue_covered.remove(tid)
2582 if tid in self.scenequeue_notneeded:
2583 self.scenequeue_notneeded.remove(tid)
2584
2585 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2586 self.sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2587
2588 if tid in self.stampcache:
2589 del self.stampcache[tid]
2590
2591 if tid in self.build_stamps:
2592 del self.build_stamps[tid]
2593
2594 update_tasks.append(tid)
2595
2596 update_tasks2 = []
2597 for tid in update_tasks:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002598 harddepfail = False
2599 for t in self.sqdata.sq_harddeps:
2600 if tid in self.sqdata.sq_harddeps[t] and t in self.scenequeue_notcovered:
2601 harddepfail = True
2602 break
2603 if not harddepfail and self.sqdata.sq_revdeps[tid].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
Brad Bishop08902b02019-08-20 09:16:51 -04002604 if tid not in self.sq_buildable:
2605 self.sq_buildable.add(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +00002606 if not self.sqdata.sq_revdeps[tid]:
Brad Bishop08902b02019-08-20 09:16:51 -04002607 self.sq_buildable.add(tid)
2608
Andrew Geissler517393d2023-01-13 08:55:19 -06002609 update_tasks2.append((tid, harddepfail, tid in self.sqdata.valid))
Brad Bishop08902b02019-08-20 09:16:51 -04002610
Andrew Geissler517393d2023-01-13 08:55:19 -06002611 if update_tasks2:
Brad Bishop08902b02019-08-20 09:16:51 -04002612 self.sqdone = False
Patrick Williams92b42cb2022-09-03 06:53:57 -05002613 for mc in sorted(self.sqdata.multiconfigs):
Andrew Geissler517393d2023-01-13 08:55:19 -06002614 for tid in sorted([t[0] for t in update_tasks2]):
Patrick Williams92b42cb2022-09-03 06:53:57 -05002615 if mc_from_tid(tid) != mc:
2616 continue
2617 h = pending_hash_index(tid, self.rqdata)
2618 if h in self.sqdata.hashes and tid != self.sqdata.hashes[h]:
2619 self.sq_deferred[tid] = self.sqdata.hashes[h]
2620 bb.note("Deferring %s after %s" % (tid, self.sqdata.hashes[h]))
Andrew Geissler517393d2023-01-13 08:55:19 -06002621 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 -05002622
Andrew Geissler517393d2023-01-13 08:55:19 -06002623 for (tid, harddepfail, origvalid) in update_tasks2:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002624 if tid in self.sqdata.valid and not origvalid:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002625 hashequiv_logger.verbose("Setscene task %s became valid" % tid)
2626 if harddepfail:
Andrew Geissler517393d2023-01-13 08:55:19 -06002627 logger.debug2("%s has an unavailable hard dependency so skipping" % (tid))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002628 self.sq_task_failoutright(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002629
2630 if changed:
Andrew Geissler5199d832021-09-24 16:47:35 -05002631 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002632 self.holdoff_need_update = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002633
Brad Bishop96ff1982019-08-19 13:50:42 -04002634 def scenequeue_updatecounters(self, task, fail=False):
Brad Bishop08902b02019-08-20 09:16:51 -04002635
2636 for dep in sorted(self.sqdata.sq_deps[task]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002637 if fail and task in self.sqdata.sq_harddeps and dep in self.sqdata.sq_harddeps[task]:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002638 if dep in self.scenequeue_covered or dep in self.scenequeue_notcovered:
2639 # dependency could be already processed, e.g. noexec setscene task
2640 continue
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002641 noexec, stamppresent = check_setscene_stamps(dep, self.rqdata, self.rq, self.stampcache)
2642 if noexec or stamppresent:
2643 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002644 logger.debug2("%s was unavailable and is a hard dependency of %s so skipping" % (task, dep))
Brad Bishop96ff1982019-08-19 13:50:42 -04002645 self.sq_task_failoutright(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002646 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002647 if self.sqdata.sq_revdeps[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2648 if dep not in self.sq_buildable:
2649 self.sq_buildable.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002650
Brad Bishop96ff1982019-08-19 13:50:42 -04002651 next = set([task])
2652 while next:
2653 new = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002654 for t in sorted(next):
Brad Bishop96ff1982019-08-19 13:50:42 -04002655 self.tasks_scenequeue_done.add(t)
2656 # Look down the dependency chain for non-setscene things which this task depends on
2657 # and mark as 'done'
2658 for dep in self.rqdata.runtaskentries[t].depends:
2659 if dep in self.rqdata.runq_setscene_tids or dep in self.tasks_scenequeue_done:
2660 continue
2661 if self.rqdata.runtaskentries[dep].revdeps.issubset(self.tasks_scenequeue_done):
2662 new.add(dep)
Brad Bishop96ff1982019-08-19 13:50:42 -04002663 next = new
2664
Andrew Geissler5199d832021-09-24 16:47:35 -05002665 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002666 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002667
2668 def sq_task_completeoutright(self, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002669 """
2670 Mark a task as completed
2671 Look at the reverse dependencies and mark any task with
2672 completed dependencies as buildable
2673 """
2674
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002675 logger.debug('Found task %s which could be accelerated', task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002676 self.scenequeue_covered.add(task)
2677 self.scenequeue_updatecounters(task)
2678
Brad Bishop96ff1982019-08-19 13:50:42 -04002679 def sq_check_taskfail(self, task):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002680 if self.rqdata.setscene_ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002681 realtask = task.split('_setscene')[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002682 (mc, fn, taskname, taskfn) = split_tid_mcfn(realtask)
2683 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002684 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002685 logger.error('Task %s.%s failed' % (pn, taskname + "_setscene"))
2686 self.rq.state = runQueueCleanUp
2687
Brad Bishop96ff1982019-08-19 13:50:42 -04002688 def sq_task_complete(self, task):
Andrew Geissler5199d832021-09-24 16:47:35 -05002689 bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
Brad Bishop96ff1982019-08-19 13:50:42 -04002690 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002691
Brad Bishop96ff1982019-08-19 13:50:42 -04002692 def sq_task_fail(self, task, result):
Andrew Geissler5199d832021-09-24 16:47:35 -05002693 bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002694 self.scenequeue_notcovered.add(task)
2695 self.scenequeue_updatecounters(task, True)
Brad Bishop96ff1982019-08-19 13:50:42 -04002696 self.sq_check_taskfail(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002697
Brad Bishop96ff1982019-08-19 13:50:42 -04002698 def sq_task_failoutright(self, task):
2699 self.sq_running.add(task)
2700 self.sq_buildable.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002701 self.scenequeue_notcovered.add(task)
2702 self.scenequeue_updatecounters(task, True)
2703
Brad Bishop96ff1982019-08-19 13:50:42 -04002704 def sq_task_skip(self, task):
2705 self.sq_running.add(task)
2706 self.sq_buildable.add(task)
2707 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002708
Brad Bishop96ff1982019-08-19 13:50:42 -04002709 def sq_build_taskdepdata(self, task):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002710 def getsetscenedeps(tid):
2711 deps = set()
2712 (mc, fn, taskname, _) = split_tid_mcfn(tid)
2713 realtid = tid + "_setscene"
2714 idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
2715 for (depname, idependtask) in idepends:
2716 if depname not in self.rqdata.taskData[mc].build_targets:
2717 continue
2718
2719 depfn = self.rqdata.taskData[mc].build_targets[depname][0]
2720 if depfn is None:
2721 continue
2722 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2723 deps.add(deptid)
2724 return deps
2725
2726 taskdepdata = {}
2727 next = getsetscenedeps(task)
2728 next.add(task)
2729 while next:
2730 additional = []
2731 for revdep in next:
2732 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2733 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2734 deps = getsetscenedeps(revdep)
2735 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2736 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002737 unihash = self.rqdata.runtaskentries[revdep].unihash
Patrick Williamsb542dec2023-06-09 01:26:37 -05002738 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2739 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002740 for revdep2 in deps:
2741 if revdep2 not in taskdepdata:
2742 additional.append(revdep2)
2743 next = additional
2744
2745 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2746 return taskdepdata
2747
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002748 def check_setscene_ignore_tasks(self, tid):
2749 # Check task that is going to run against the ignore tasks list
Brad Bishop96ff1982019-08-19 13:50:42 -04002750 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2751 # Ignore covered tasks
2752 if tid in self.tasks_covered:
2753 return False
2754 # Ignore stamped tasks
2755 if self.rq.check_stamp_task(tid, taskname, cache=self.stampcache):
2756 return False
2757 # Ignore noexec tasks
2758 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2759 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2760 return False
2761
2762 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002763 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Brad Bishop96ff1982019-08-19 13:50:42 -04002764 if tid in self.rqdata.runq_setscene_tids:
Andrew Geissler595f6302022-01-24 19:11:47 +00002765 msg = ['Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)]
Brad Bishop96ff1982019-08-19 13:50:42 -04002766 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002767 msg = ['Task %s.%s attempted to execute unexpectedly' % (pn, taskname)]
Andrew Geissler82c905d2020-04-13 13:39:40 -05002768 for t in self.scenequeue_notcovered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002769 msg.append("\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash))
2770 msg.append('\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered))
2771 logger.error("".join(msg))
Brad Bishop96ff1982019-08-19 13:50:42 -04002772 return True
2773 return False
2774
2775class SQData(object):
2776 def __init__(self):
2777 # SceneQueue dependencies
2778 self.sq_deps = {}
2779 # SceneQueue reverse dependencies
2780 self.sq_revdeps = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002781 # Injected inter-setscene task dependencies
2782 self.sq_harddeps = {}
2783 # Cache of stamp files so duplicates can't run in parallel
2784 self.stamps = {}
2785 # Setscene tasks directly depended upon by the build
2786 self.unskippable = set()
2787 # List of setscene tasks which aren't present
2788 self.outrightfail = set()
2789 # A list of normal tasks a setscene task covers
2790 self.sq_covered_tasks = {}
2791
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002792def build_scenequeue_data(sqdata, rqdata, sqrq):
Brad Bishop96ff1982019-08-19 13:50:42 -04002793
2794 sq_revdeps = {}
2795 sq_revdeps_squash = {}
2796 sq_collated_deps = {}
2797
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002798 # We can't skip specified target tasks which aren't setscene tasks
2799 sqdata.cantskip = set(rqdata.target_tids)
2800 sqdata.cantskip.difference_update(rqdata.runq_setscene_tids)
2801 sqdata.cantskip.intersection_update(rqdata.runtaskentries)
2802
Brad Bishop96ff1982019-08-19 13:50:42 -04002803 # We need to construct a dependency graph for the setscene functions. Intermediate
2804 # dependencies between the setscene tasks only complicate the code. This code
2805 # therefore aims to collapse the huge runqueue dependency tree into a smaller one
2806 # only containing the setscene functions.
2807
2808 rqdata.init_progress_reporter.next_stage()
2809
2810 # First process the chains up to the first setscene task.
2811 endpoints = {}
2812 for tid in rqdata.runtaskentries:
2813 sq_revdeps[tid] = copy.copy(rqdata.runtaskentries[tid].revdeps)
2814 sq_revdeps_squash[tid] = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00002815 if not sq_revdeps[tid] and tid not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002816 #bb.warn("Added endpoint %s" % (tid))
2817 endpoints[tid] = set()
2818
2819 rqdata.init_progress_reporter.next_stage()
2820
2821 # Secondly process the chains between setscene tasks.
2822 for tid in rqdata.runq_setscene_tids:
2823 sq_collated_deps[tid] = set()
2824 #bb.warn("Added endpoint 2 %s" % (tid))
2825 for dep in rqdata.runtaskentries[tid].depends:
2826 if tid in sq_revdeps[dep]:
2827 sq_revdeps[dep].remove(tid)
2828 if dep not in endpoints:
2829 endpoints[dep] = set()
2830 #bb.warn(" Added endpoint 3 %s" % (dep))
2831 endpoints[dep].add(tid)
2832
2833 rqdata.init_progress_reporter.next_stage()
2834
2835 def process_endpoints(endpoints):
2836 newendpoints = {}
2837 for point, task in endpoints.items():
2838 tasks = set()
2839 if task:
2840 tasks |= task
2841 if sq_revdeps_squash[point]:
2842 tasks |= sq_revdeps_squash[point]
2843 if point not in rqdata.runq_setscene_tids:
2844 for t in tasks:
2845 sq_collated_deps[t].add(point)
2846 sq_revdeps_squash[point] = set()
2847 if point in rqdata.runq_setscene_tids:
2848 sq_revdeps_squash[point] = tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002849 continue
2850 for dep in rqdata.runtaskentries[point].depends:
2851 if point in sq_revdeps[dep]:
2852 sq_revdeps[dep].remove(point)
2853 if tasks:
2854 sq_revdeps_squash[dep] |= tasks
Andrew Geissler595f6302022-01-24 19:11:47 +00002855 if not sq_revdeps[dep] and dep not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002856 newendpoints[dep] = task
Andrew Geissler595f6302022-01-24 19:11:47 +00002857 if newendpoints:
Brad Bishop96ff1982019-08-19 13:50:42 -04002858 process_endpoints(newendpoints)
2859
2860 process_endpoints(endpoints)
2861
2862 rqdata.init_progress_reporter.next_stage()
2863
Brad Bishop08902b02019-08-20 09:16:51 -04002864 # Build a list of tasks which are "unskippable"
2865 # These are direct endpoints referenced by the build upto and including setscene tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002866 # Take the build endpoints (no revdeps) and find the sstate tasks they depend upon
2867 new = True
2868 for tid in rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00002869 if not rqdata.runtaskentries[tid].revdeps:
Brad Bishop96ff1982019-08-19 13:50:42 -04002870 sqdata.unskippable.add(tid)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002871 sqdata.unskippable |= sqdata.cantskip
Brad Bishop96ff1982019-08-19 13:50:42 -04002872 while new:
2873 new = False
Brad Bishop08902b02019-08-20 09:16:51 -04002874 orig = sqdata.unskippable.copy()
2875 for tid in sorted(orig, reverse=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04002876 if tid in rqdata.runq_setscene_tids:
2877 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00002878 if not rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04002879 # These are tasks which have no setscene tasks in their chain, need to mark as directly buildable
Brad Bishop96ff1982019-08-19 13:50:42 -04002880 sqrq.setbuildable(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002881 sqdata.unskippable |= rqdata.runtaskentries[tid].depends
Brad Bishop08902b02019-08-20 09:16:51 -04002882 if sqdata.unskippable != orig:
2883 new = True
2884
2885 sqrq.tasks_scenequeue_done |= sqdata.unskippable.difference(rqdata.runq_setscene_tids)
Brad Bishop96ff1982019-08-19 13:50:42 -04002886
2887 rqdata.init_progress_reporter.next_stage(len(rqdata.runtaskentries))
2888
2889 # Sanity check all dependencies could be changed to setscene task references
2890 for taskcounter, tid in enumerate(rqdata.runtaskentries):
2891 if tid in rqdata.runq_setscene_tids:
2892 pass
Andrew Geissler595f6302022-01-24 19:11:47 +00002893 elif sq_revdeps_squash[tid]:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002894 bb.msg.fatal("RunQueue", "Something went badly wrong during scenequeue generation, halting. Please report this problem.")
Brad Bishop96ff1982019-08-19 13:50:42 -04002895 else:
2896 del sq_revdeps_squash[tid]
2897 rqdata.init_progress_reporter.update(taskcounter)
2898
2899 rqdata.init_progress_reporter.next_stage()
2900
2901 # Resolve setscene inter-task dependencies
2902 # e.g. do_sometask_setscene[depends] = "targetname:do_someothertask_setscene"
2903 # Note that anything explicitly depended upon will have its reverse dependencies removed to avoid circular dependencies
2904 for tid in rqdata.runq_setscene_tids:
2905 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2906 realtid = tid + "_setscene"
2907 idepends = rqdata.taskData[mc].taskentries[realtid].idepends
Andrew Geissler517393d2023-01-13 08:55:19 -06002908 sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2909
Brad Bishop96ff1982019-08-19 13:50:42 -04002910 for (depname, idependtask) in idepends:
2911
2912 if depname not in rqdata.taskData[mc].build_targets:
2913 continue
2914
2915 depfn = rqdata.taskData[mc].build_targets[depname][0]
2916 if depfn is None:
2917 continue
2918 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2919 if deptid not in rqdata.runtaskentries:
2920 bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (realtid, depfn, idependtask))
2921
2922 if not deptid in sqdata.sq_harddeps:
2923 sqdata.sq_harddeps[deptid] = set()
2924 sqdata.sq_harddeps[deptid].add(tid)
2925
2926 sq_revdeps_squash[tid].add(deptid)
2927 # Have to zero this to avoid circular dependencies
2928 sq_revdeps_squash[deptid] = set()
2929
2930 rqdata.init_progress_reporter.next_stage()
2931
2932 for task in sqdata.sq_harddeps:
2933 for dep in sqdata.sq_harddeps[task]:
2934 sq_revdeps_squash[dep].add(task)
2935
2936 rqdata.init_progress_reporter.next_stage()
2937
2938 #for tid in sq_revdeps_squash:
2939 # data = ""
2940 # for dep in sq_revdeps_squash[tid]:
2941 # data = data + "\n %s" % dep
2942 # bb.warn("Task %s_setscene: is %s " % (tid, data))
2943
2944 sqdata.sq_revdeps = sq_revdeps_squash
Brad Bishop96ff1982019-08-19 13:50:42 -04002945 sqdata.sq_covered_tasks = sq_collated_deps
2946
2947 # Build reverse version of revdeps to populate deps structure
2948 for tid in sqdata.sq_revdeps:
2949 sqdata.sq_deps[tid] = set()
2950 for tid in sqdata.sq_revdeps:
2951 for dep in sqdata.sq_revdeps[tid]:
2952 sqdata.sq_deps[dep].add(tid)
2953
2954 rqdata.init_progress_reporter.next_stage()
2955
Brad Bishop00e122a2019-10-05 11:10:57 -04002956 sqdata.multiconfigs = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002957 for tid in sqdata.sq_revdeps:
Brad Bishop00e122a2019-10-05 11:10:57 -04002958 sqdata.multiconfigs.add(mc_from_tid(tid))
Andrew Geissler595f6302022-01-24 19:11:47 +00002959 if not sqdata.sq_revdeps[tid]:
Brad Bishop96ff1982019-08-19 13:50:42 -04002960 sqrq.sq_buildable.add(tid)
2961
2962 rqdata.init_progress_reporter.finish()
2963
Brad Bishop00e122a2019-10-05 11:10:57 -04002964 sqdata.noexec = set()
2965 sqdata.stamppresent = set()
2966 sqdata.valid = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002967
Patrick Williams213cb262021-08-07 19:21:33 -05002968 sqdata.hashes = {}
2969 sqrq.sq_deferred = {}
2970 for mc in sorted(sqdata.multiconfigs):
2971 for tid in sorted(sqdata.sq_revdeps):
2972 if mc_from_tid(tid) != mc:
2973 continue
2974 h = pending_hash_index(tid, rqdata)
2975 if h not in sqdata.hashes:
2976 sqdata.hashes[h] = tid
2977 else:
2978 sqrq.sq_deferred[tid] = sqdata.hashes[h]
Andrew Geissler8f840682023-07-21 09:09:43 -05002979 bb.debug(1, "Deferring %s after %s" % (tid, sqdata.hashes[h]))
Patrick Williams213cb262021-08-07 19:21:33 -05002980
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002981def check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=False):
2982
2983 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2984
2985 taskdep = rqdata.dataCaches[mc].task_deps[taskfn]
2986
2987 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Andrew Geissler517393d2023-01-13 08:55:19 -06002988 bb.build.make_stamp_mcfn(taskname + "_setscene", taskfn)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002989 return True, False
2990
2991 if rq.check_stamp_task(tid, taskname + "_setscene", cache=stampcache):
2992 logger.debug2('Setscene stamp current for task %s', tid)
2993 return False, True
2994
2995 if rq.check_stamp_task(tid, taskname, recurse = True, cache=stampcache):
2996 logger.debug2('Normal stamp current for task %s', tid)
2997 return False, True
2998
2999 return False, False
3000
Brad Bishop1d80a2e2019-11-15 16:35:03 -05003001def update_scenequeue_data(tids, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True):
Brad Bishop00e122a2019-10-05 11:10:57 -04003002
3003 tocheck = set()
3004
3005 for tid in sorted(tids):
3006 if tid in sqdata.stamppresent:
3007 sqdata.stamppresent.remove(tid)
3008 if tid in sqdata.valid:
3009 sqdata.valid.remove(tid)
Andrew Geisslerc926e172021-05-07 16:11:35 -05003010 if tid in sqdata.outrightfail:
3011 sqdata.outrightfail.remove(tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04003012
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003013 noexec, stamppresent = check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04003014
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003015 if noexec:
Brad Bishop00e122a2019-10-05 11:10:57 -04003016 sqdata.noexec.add(tid)
3017 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003018 logger.debug2("%s is noexec so skipping setscene" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04003019 continue
3020
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003021 if stamppresent:
Brad Bishop00e122a2019-10-05 11:10:57 -04003022 sqdata.stamppresent.add(tid)
3023 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003024 logger.debug2("%s has a valid stamp, skipping" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04003025 continue
3026
3027 tocheck.add(tid)
3028
Brad Bishop1d80a2e2019-11-15 16:35:03 -05003029 sqdata.valid |= rq.validate_hashes(tocheck, cooker.data, len(sqdata.stamppresent), False, summary=summary)
Brad Bishop00e122a2019-10-05 11:10:57 -04003030
Patrick Williams213cb262021-08-07 19:21:33 -05003031 for tid in tids:
3032 if tid in sqdata.stamppresent:
3033 continue
3034 if tid in sqdata.valid:
3035 continue
3036 if tid in sqdata.noexec:
3037 continue
3038 if tid in sqrq.scenequeue_covered:
3039 continue
3040 if tid in sqrq.scenequeue_notcovered:
3041 continue
3042 if tid in sqrq.sq_deferred:
3043 continue
3044 sqdata.outrightfail.add(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003045 logger.debug2("%s already handled (fallthrough), skipping" % (tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04003046
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003047class TaskFailure(Exception):
3048 """
3049 Exception raised when a task in a runqueue fails
3050 """
3051 def __init__(self, x):
3052 self.args = x
3053
3054
3055class runQueueExitWait(bb.event.Event):
3056 """
3057 Event when waiting for task processes to exit
3058 """
3059
3060 def __init__(self, remain):
3061 self.remain = remain
3062 self.message = "Waiting for %s active tasks to finish" % remain
3063 bb.event.Event.__init__(self)
3064
3065class runQueueEvent(bb.event.Event):
3066 """
3067 Base runQueue event class
3068 """
3069 def __init__(self, task, stats, rq):
3070 self.taskid = task
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003071 self.taskstring = task
3072 self.taskname = taskname_from_tid(task)
3073 self.taskfile = fn_from_tid(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003074 self.taskhash = rq.rqdata.get_task_hash(task)
3075 self.stats = stats.copy()
3076 bb.event.Event.__init__(self)
3077
3078class sceneQueueEvent(runQueueEvent):
3079 """
3080 Base sceneQueue event class
3081 """
3082 def __init__(self, task, stats, rq, noexec=False):
3083 runQueueEvent.__init__(self, task, stats, rq)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003084 self.taskstring = task + "_setscene"
3085 self.taskname = taskname_from_tid(task) + "_setscene"
3086 self.taskfile = fn_from_tid(task)
3087 self.taskhash = rq.rqdata.get_task_hash(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003088
3089class runQueueTaskStarted(runQueueEvent):
3090 """
3091 Event notifying a task was started
3092 """
3093 def __init__(self, task, stats, rq, noexec=False):
3094 runQueueEvent.__init__(self, task, stats, rq)
3095 self.noexec = noexec
3096
3097class sceneQueueTaskStarted(sceneQueueEvent):
3098 """
3099 Event notifying a setscene task was started
3100 """
3101 def __init__(self, task, stats, rq, noexec=False):
3102 sceneQueueEvent.__init__(self, task, stats, rq)
3103 self.noexec = noexec
3104
3105class runQueueTaskFailed(runQueueEvent):
3106 """
3107 Event notifying a task failed
3108 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003109 def __init__(self, task, stats, exitcode, rq, fakeroot_log=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003110 runQueueEvent.__init__(self, task, stats, rq)
3111 self.exitcode = exitcode
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003112 self.fakeroot_log = fakeroot_log
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003113
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003114 def __str__(self):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003115 if self.fakeroot_log:
3116 return "Task (%s) failed with exit code '%s' \nPseudo log:\n%s" % (self.taskstring, self.exitcode, self.fakeroot_log)
3117 else:
3118 return "Task (%s) failed with exit code '%s'" % (self.taskstring, self.exitcode)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003119
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003120class sceneQueueTaskFailed(sceneQueueEvent):
3121 """
3122 Event notifying a setscene task failed
3123 """
3124 def __init__(self, task, stats, exitcode, rq):
3125 sceneQueueEvent.__init__(self, task, stats, rq)
3126 self.exitcode = exitcode
3127
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003128 def __str__(self):
3129 return "Setscene task (%s) failed with exit code '%s' - real task will be run instead" % (self.taskstring, self.exitcode)
3130
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003131class sceneQueueComplete(sceneQueueEvent):
3132 """
3133 Event when all the sceneQueue tasks are complete
3134 """
3135 def __init__(self, stats, rq):
3136 self.stats = stats.copy()
3137 bb.event.Event.__init__(self)
3138
3139class runQueueTaskCompleted(runQueueEvent):
3140 """
3141 Event notifying a task completed
3142 """
3143
3144class sceneQueueTaskCompleted(sceneQueueEvent):
3145 """
3146 Event notifying a setscene task completed
3147 """
3148
3149class runQueueTaskSkipped(runQueueEvent):
3150 """
3151 Event notifying a task was skipped
3152 """
3153 def __init__(self, task, stats, rq, reason):
3154 runQueueEvent.__init__(self, task, stats, rq)
3155 self.reason = reason
3156
Brad Bishop08902b02019-08-20 09:16:51 -04003157class taskUniHashUpdate(bb.event.Event):
3158 """
3159 Base runQueue event class
3160 """
3161 def __init__(self, task, unihash):
3162 self.taskid = task
3163 self.unihash = unihash
3164 bb.event.Event.__init__(self)
3165
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003166class runQueuePipe():
3167 """
3168 Abstraction for a pipe between a worker thread and the server
3169 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003170 def __init__(self, pipein, pipeout, d, rq, rqexec, fakerootlogs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003171 self.input = pipein
3172 if pipeout:
3173 pipeout.close()
3174 bb.utils.nonblockingfd(self.input)
Andrew Geissler220dafd2023-10-04 10:18:08 -05003175 self.queue = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003176 self.d = d
3177 self.rq = rq
3178 self.rqexec = rqexec
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003179 self.fakerootlogs = fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003180
3181 def setrunqueueexec(self, rqexec):
3182 self.rqexec = rqexec
3183
3184 def read(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003185 for workers, name in [(self.rq.worker, "Worker"), (self.rq.fakeworker, "Fakeroot")]:
3186 for worker in workers.values():
3187 worker.process.poll()
3188 if worker.process.returncode is not None and not self.rq.teardown:
3189 bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, worker.process.pid, str(worker.process.returncode)))
3190 self.rq.finish_runqueue(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003191
3192 start = len(self.queue)
3193 try:
Andrew Geissler220dafd2023-10-04 10:18:08 -05003194 self.queue.extend(self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003195 except (OSError, IOError) as e:
3196 if e.errno != errno.EAGAIN:
3197 raise
3198 end = len(self.queue)
3199 found = True
Andrew Geissler595f6302022-01-24 19:11:47 +00003200 while found and self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003201 found = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003202 index = self.queue.find(b"</event>")
3203 while index != -1 and self.queue.startswith(b"<event>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003204 try:
3205 event = pickle.loads(self.queue[7:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003206 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
3207 if isinstance(e, pickle.UnpicklingError) and "truncated" in str(e):
3208 # The pickled data could contain "</event>" so search for the next occurance
3209 # unpickling again, this should be the only way an unpickle error could occur
3210 index = self.queue.find(b"</event>", index + 1)
3211 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003212 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
3213 bb.event.fire_from_worker(event, self.d)
Brad Bishop08902b02019-08-20 09:16:51 -04003214 if isinstance(event, taskUniHashUpdate):
3215 self.rqexec.updated_taskhash_queue.append((event.taskid, event.unihash))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003216 found = True
3217 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003218 index = self.queue.find(b"</event>")
3219 index = self.queue.find(b"</exitcode>")
3220 while index != -1 and self.queue.startswith(b"<exitcode>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003221 try:
3222 task, status = pickle.loads(self.queue[10:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003223 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003224 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003225 (_, _, _, taskfn) = split_tid_mcfn(task)
3226 fakerootlog = None
3227 if self.fakerootlogs and taskfn and taskfn in self.fakerootlogs:
3228 fakerootlog = self.fakerootlogs[taskfn]
3229 self.rqexec.runqueue_process_waitpid(task, status, fakerootlog=fakerootlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003230 found = True
3231 self.queue = self.queue[index+11:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003232 index = self.queue.find(b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003233 return (end > start)
3234
3235 def close(self):
3236 while self.read():
3237 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00003238 if self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003239 print("Warning, worker left partial message: %s" % self.queue)
3240 self.input.close()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003241
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003242def get_setscene_enforce_ignore_tasks(d, targets):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003243 if d.getVar('BB_SETSCENE_ENFORCE') != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003244 return None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003245 ignore_tasks = (d.getVar("BB_SETSCENE_ENFORCE_IGNORE_TASKS") or "").split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003246 outlist = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003247 for item in ignore_tasks[:]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003248 if item.startswith('%:'):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003249 for (mc, target, task, fn) in targets:
3250 outlist.append(target + ':' + item.split(':')[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003251 else:
3252 outlist.append(item)
3253 return outlist
3254
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003255def check_setscene_enforce_ignore_tasks(pn, taskname, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003256 import fnmatch
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003257 if ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003258 item = '%s:%s' % (pn, taskname)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003259 for ignore_tasks in ignore_tasks:
3260 if fnmatch.fnmatch(item, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003261 return True
3262 return False
3263 return True