blob: af11e9a8f49ee8a7cb09ce6cc9c4eef91d235e9f [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
Brad Bishop316dfdd2018-06-25 12:45:53 -04001007
Patrick Williams705982a2024-01-12 09:51:57 -06001008 runall_tids = set()
1009 added = True
1010 while added:
1011 reduced_tasklist = set(self.runtaskentries.keys())
1012 for tid in list(self.runtaskentries.keys()):
1013 if tid not in runq_build:
1014 reduced_tasklist.remove(tid)
1015 runq_build = {}
1016
1017 orig = runall_tids
Brad Bishop316dfdd2018-06-25 12:45:53 -04001018 runall_tids = set()
Patrick Williams705982a2024-01-12 09:51:57 -06001019 for task in self.cooker.configuration.runall:
1020 if not task.startswith("do_"):
1021 task = "do_{0}".format(task)
1022 for tid in reduced_tasklist:
1023 wanttid = "{0}:{1}".format(fn_from_tid(tid), task)
1024 if wanttid in self.runtaskentries:
1025 runall_tids.add(wanttid)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001026
Patrick Williams705982a2024-01-12 09:51:57 -06001027 for tid in list(runall_tids):
1028 mark_active(tid, 1)
1029 self.target_tids.append(tid)
1030 if self.cooker.configuration.force:
1031 invalidate_task(tid, False)
1032 added = runall_tids - orig
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001033
Andrew Geissler595f6302022-01-24 19:11:47 +00001034 delcount = set()
1035 for tid in list(self.runtaskentries.keys()):
1036 if tid not in runq_build:
1037 delcount.add(tid)
1038 del self.runtaskentries[tid]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001039
Andrew Geissler595f6302022-01-24 19:11:47 +00001040 if self.cooker.configuration.runall:
1041 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001042 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)))
1043
1044 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001045 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001046
1047 # Handle runonly
1048 if self.cooker.configuration.runonly:
1049 # re-run the mark_active and then drop unused tasks from new list
1050 runq_build = {}
1051
1052 for task in self.cooker.configuration.runonly:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001053 if not task.startswith("do_"):
1054 task = "do_{0}".format(task)
Andrew Geissler595f6302022-01-24 19:11:47 +00001055 runonly_tids = [k for k in self.runtaskentries.keys() if taskname_from_tid(k) == task]
Brad Bishop316dfdd2018-06-25 12:45:53 -04001056
Andrew Geissler595f6302022-01-24 19:11:47 +00001057 for tid in runonly_tids:
1058 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001059 if self.cooker.configuration.force:
1060 invalidate_task(tid, False)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001061
1062 for tid in list(self.runtaskentries.keys()):
1063 if tid not in runq_build:
Andrew Geissler595f6302022-01-24 19:11:47 +00001064 delcount.add(tid)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001065 del self.runtaskentries[tid]
1066
Andrew Geissler595f6302022-01-24 19:11:47 +00001067 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001068 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 -05001069
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001070 #
1071 # Step D - Sanity checks and computation
1072 #
1073
1074 # Check to make sure we still have tasks to run
Andrew Geissler595f6302022-01-24 19:11:47 +00001075 if not self.runtaskentries:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001076 if not taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001077 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.")
1078 else:
1079 bb.msg.fatal("RunQueue", "No active tasks and not in --continue mode?! Please report this bug.")
1080
Brad Bishop316dfdd2018-06-25 12:45:53 -04001081 logger.verbose("Pruned %s inactive tasks, %s left", len(delcount), len(self.runtaskentries))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001082
1083 logger.verbose("Assign Weightings")
1084
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001085 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001086 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001087
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001088 # Generate a list of reverse dependencies to ease future calculations
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001089 for tid in self.runtaskentries:
1090 for dep in self.runtaskentries[tid].depends:
1091 self.runtaskentries[dep].revdeps.add(tid)
1092
1093 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001094 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001095
1096 # Identify tasks at the end of dependency chains
1097 # Error on circular dependency loops (length two)
1098 endpoints = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001099 for tid in self.runtaskentries:
1100 revdeps = self.runtaskentries[tid].revdeps
Andrew Geissler595f6302022-01-24 19:11:47 +00001101 if not revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001102 endpoints.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001103 for dep in revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001104 if dep in self.runtaskentries[tid].depends:
1105 bb.msg.fatal("RunQueue", "Task %s has circular dependency on %s" % (tid, dep))
1106
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001107
1108 logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
1109
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001110 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001111 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001112
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001113 # Calculate task weights
1114 # Check of higher length circular dependencies
1115 self.runq_weight = self.calculate_task_weights(endpoints)
1116
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001117 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001118 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001119
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001120 # Sanity Check - Check for multiple tasks building the same provider
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001121 for mc in self.dataCaches:
1122 prov_list = {}
1123 seen_fn = []
1124 for tid in self.runtaskentries:
1125 (tidmc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1126 if taskfn in seen_fn:
1127 continue
1128 if mc != tidmc:
1129 continue
1130 seen_fn.append(taskfn)
1131 for prov in self.dataCaches[mc].fn_provides[taskfn]:
1132 if prov not in prov_list:
1133 prov_list[prov] = [taskfn]
1134 elif taskfn not in prov_list[prov]:
1135 prov_list[prov].append(taskfn)
1136 for prov in prov_list:
1137 if len(prov_list[prov]) < 2:
1138 continue
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001139 if prov in self.multi_provider_allowed:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001140 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001141 seen_pn = []
1142 # If two versions of the same PN are being built its fatal, we don't support it.
1143 for fn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001144 pn = self.dataCaches[mc].pkg_fn[fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001145 if pn not in seen_pn:
1146 seen_pn.append(pn)
1147 else:
1148 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 +00001149 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 -05001150 #
1151 # Construct a list of things which uniquely depend on each provider
1152 # since this may help the user figure out which dependency is triggering this warning
1153 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001154 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 -05001155 deplist = {}
1156 commondeps = None
1157 for provfn in prov_list[prov]:
1158 deps = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001159 for tid in self.runtaskentries:
1160 fn = fn_from_tid(tid)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001161 if fn != provfn:
1162 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001163 for dep in self.runtaskentries[tid].revdeps:
1164 fn = fn_from_tid(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001165 if fn == provfn:
1166 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001167 deps.add(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001168 if not commondeps:
1169 commondeps = set(deps)
1170 else:
1171 commondeps &= deps
1172 deplist[provfn] = deps
1173 for provfn in deplist:
Andrew Geissler595f6302022-01-24 19:11:47 +00001174 msgs.append("\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001175 #
1176 # Construct a list of provides and runtime providers for each recipe
1177 # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC)
1178 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001179 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 -05001180 provide_results = {}
1181 rprovide_results = {}
1182 commonprovs = None
1183 commonrprovs = None
1184 for provfn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001185 provides = set(self.dataCaches[mc].fn_provides[provfn])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001186 rprovides = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001187 for rprovide in self.dataCaches[mc].rproviders:
1188 if provfn in self.dataCaches[mc].rproviders[rprovide]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001189 rprovides.add(rprovide)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001190 for package in self.dataCaches[mc].packages:
1191 if provfn in self.dataCaches[mc].packages[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001192 rprovides.add(package)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001193 for package in self.dataCaches[mc].packages_dynamic:
1194 if provfn in self.dataCaches[mc].packages_dynamic[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001195 rprovides.add(package)
1196 if not commonprovs:
1197 commonprovs = set(provides)
1198 else:
1199 commonprovs &= provides
1200 provide_results[provfn] = provides
1201 if not commonrprovs:
1202 commonrprovs = set(rprovides)
1203 else:
1204 commonrprovs &= rprovides
1205 rprovide_results[provfn] = rprovides
Andrew Geissler595f6302022-01-24 19:11:47 +00001206 #msgs.append("\nCommon provides:\n %s" % ("\n ".join(commonprovs)))
1207 #msgs.append("\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001208 for provfn in prov_list[prov]:
Andrew Geissler595f6302022-01-24 19:11:47 +00001209 msgs.append("\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs)))
1210 msgs.append("\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001211
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001212 if self.warn_multi_bb:
Andrew Geissler595f6302022-01-24 19:11:47 +00001213 logger.verbnote("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001214 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00001215 logger.error("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001216
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001217 self.init_progress_reporter.next_stage()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001218 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001219 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001220
1221 # Iterate over the task list looking for tasks with a 'setscene' function
Andrew Geissler82c905d2020-04-13 13:39:40 -05001222 self.runq_setscene_tids = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001223 if not self.cooker.configuration.nosetscene:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001224 for tid in self.runtaskentries:
1225 (mc, fn, taskname, _) = split_tid_mcfn(tid)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001226 setscenetid = tid + "_setscene"
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001227 if setscenetid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001228 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -05001229 self.runq_setscene_tids.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001230
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001231 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001232 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001233
1234 # Invalidate task if force mode active
1235 if self.cooker.configuration.force:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001236 for tid in self.target_tids:
1237 invalidate_task(tid, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001238
1239 # Invalidate task if invalidate mode active
1240 if self.cooker.configuration.invalidate_stamp:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001241 for tid in self.target_tids:
1242 fn = fn_from_tid(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001243 for st in self.cooker.configuration.invalidate_stamp.split(','):
1244 if not st.startswith("do_"):
1245 st = "do_%s" % st
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001246 invalidate_task(fn + ":" + st, True)
1247
1248 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001249 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001250
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001251 # Create and print to the logs a virtual/xxxx -> PN (fn) table
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001252 for mc in taskData:
1253 virtmap = taskData[mc].get_providermap(prefix="virtual/")
1254 virtpnmap = {}
1255 for v in virtmap:
1256 virtpnmap[v] = self.dataCaches[mc].pkg_fn[virtmap[v]]
1257 bb.debug(2, "%s resolved to: %s (%s)" % (v, virtpnmap[v], virtmap[v]))
1258 if hasattr(bb.parse.siggen, "tasks_resolved"):
1259 bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
1260
1261 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001262 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001263
Brad Bishop00e122a2019-10-05 11:10:57 -04001264 bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
1265
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001266 # Iterate over the task list and call into the siggen code
1267 dealtwith = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001268 todeal = set(self.runtaskentries)
Andrew Geissler595f6302022-01-24 19:11:47 +00001269 while todeal:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001270 for tid in todeal.copy():
Andrew Geissler595f6302022-01-24 19:11:47 +00001271 if not (self.runtaskentries[tid].depends - dealtwith):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001272 dealtwith.add(tid)
1273 todeal.remove(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001274 self.prepare_task_hash(tid)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001275 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001276
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001277 bb.parse.siggen.writeout_file_checksum_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001278
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001279 #self.dump_data()
1280 return len(self.runtaskentries)
1281
Brad Bishop19323692019-04-05 15:28:33 -04001282 def prepare_task_hash(self, tid):
Andrew Geissler517393d2023-01-13 08:55:19 -06001283 bb.parse.siggen.prep_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
1284 self.runtaskentries[tid].hash = bb.parse.siggen.get_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
Brad Bishop08902b02019-08-20 09:16:51 -04001285 self.runtaskentries[tid].unihash = bb.parse.siggen.get_unihash(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001286
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001287 def dump_data(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001288 """
1289 Dump some debug information on the internal data structures
1290 """
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001291 logger.debug3("run_tasks:")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001292 for tid in self.runtaskentries:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001293 logger.debug3(" %s: %s Deps %s RevDeps %s", tid,
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001294 self.runtaskentries[tid].weight,
1295 self.runtaskentries[tid].depends,
1296 self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001297
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001298class RunQueueWorker():
1299 def __init__(self, process, pipe):
1300 self.process = process
1301 self.pipe = pipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001302
1303class RunQueue:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001304 def __init__(self, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001305
1306 self.cooker = cooker
1307 self.cfgData = cfgData
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001308 self.rqdata = RunQueueData(self, cooker, cfgData, dataCaches, taskData, targets)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001309
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001310 self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION") or None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001311 self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID") or None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001312
1313 self.state = runQueuePrepare
1314
1315 # For disk space monitor
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001316 # Invoked at regular time intervals via the bitbake heartbeat event
1317 # while the build is running. We generate a unique name for the handler
1318 # here, just in case that there ever is more than one RunQueue instance,
Brad Bishop96ff1982019-08-19 13:50:42 -04001319 # start the handler when reaching runQueueSceneInit, and stop it when
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001320 # done with the build.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001321 self.dm = monitordisk.diskMonitor(cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001322 self.dm_event_handler_name = '_bb_diskmonitor_' + str(id(self))
1323 self.dm_event_handler_registered = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001324 self.rqexe = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001325 self.worker = {}
1326 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001327
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001328 @staticmethod
1329 def send_pickled_data(worker, data, name):
1330 msg = bytearray()
1331 msg.extend(b"<" + name.encode() + b">")
1332 pickled_data = pickle.dumps(data)
1333 msg.extend(len(pickled_data).to_bytes(4, 'big'))
1334 msg.extend(pickled_data)
1335 msg.extend(b"</" + name.encode() + b">")
1336 worker.stdin.write(msg)
1337
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001338 def _start_worker(self, mc, fakeroot = False, rqexec = None):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001339 logger.debug("Starting bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001340 magic = "decafbad"
1341 if self.cooker.configuration.profile:
1342 magic = "decafbadbad"
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001343 fakerootlogs = None
Andrew Geissler220dafd2023-10-04 10:18:08 -05001344
1345 workerscript = os.path.realpath(os.path.dirname(__file__) + "/../../bin/bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001346 if fakeroot:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001347 magic = magic + "beef"
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001348 mcdata = self.cooker.databuilder.mcdata[mc]
Brad Bishop19323692019-04-05 15:28:33 -04001349 fakerootcmd = shlex.split(mcdata.getVar("FAKEROOTCMD"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001350 fakerootenv = (mcdata.getVar("FAKEROOTBASEENV") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001351 env = os.environ.copy()
1352 for key, value in (var.split('=') for var in fakerootenv):
1353 env[key] = value
Andrew Geissler220dafd2023-10-04 10:18:08 -05001354 worker = subprocess.Popen(fakerootcmd + [sys.executable, workerscript, magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001355 fakerootlogs = self.rqdata.dataCaches[mc].fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001356 else:
Andrew Geissler220dafd2023-10-04 10:18:08 -05001357 worker = subprocess.Popen([sys.executable, workerscript, magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001358 bb.utils.nonblockingfd(worker.stdout)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001359 workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, self, rqexec, fakerootlogs=fakerootlogs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001360
1361 workerdata = {
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001362 "sigdata" : bb.parse.siggen.get_taskdata(),
Andrew Geissler82c905d2020-04-13 13:39:40 -05001363 "logdefaultlevel" : bb.msg.loggerDefaultLogLevel,
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001364 "build_verbose_shell" : self.cooker.configuration.build_verbose_shell,
1365 "build_verbose_stdout" : self.cooker.configuration.build_verbose_stdout,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001366 "logdefaultdomain" : bb.msg.loggerDefaultDomains,
1367 "prhost" : self.cooker.prhost,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001368 "buildname" : self.cfgData.getVar("BUILDNAME"),
1369 "date" : self.cfgData.getVar("DATE"),
1370 "time" : self.cfgData.getVar("TIME"),
Brad Bishopa34c0302019-09-23 22:34:48 -04001371 "hashservaddr" : self.cooker.hashservaddr,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001372 "umask" : self.cfgData.getVar("BB_DEFAULT_UMASK"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001373 }
1374
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001375 RunQueue.send_pickled_data(worker, self.cooker.configuration, "cookerconfig")
1376 RunQueue.send_pickled_data(worker, self.cooker.extraconfigdata, "extraconfigdata")
1377 RunQueue.send_pickled_data(worker, workerdata, "workerdata")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001378 worker.stdin.flush()
1379
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001380 return RunQueueWorker(worker, workerpipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001381
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001382 def _teardown_worker(self, worker):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001383 if not worker:
1384 return
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001385 logger.debug("Teardown for bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001386 try:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001387 RunQueue.send_pickled_data(worker.process, b"", "quit")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001388 worker.process.stdin.flush()
1389 worker.process.stdin.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001390 except IOError:
1391 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001392 while worker.process.returncode is None:
1393 worker.pipe.read()
1394 worker.process.poll()
1395 while worker.pipe.read():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001396 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001397 worker.pipe.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001398
Patrick Williams169d7bc2024-01-05 11:33:25 -06001399 def start_worker(self, rqexec):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001400 if self.worker:
1401 self.teardown_workers()
1402 self.teardown = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001403 for mc in self.rqdata.dataCaches:
Patrick Williams169d7bc2024-01-05 11:33:25 -06001404 self.worker[mc] = self._start_worker(mc, False, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001405
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001406 def start_fakeworker(self, rqexec, mc):
1407 if not mc in self.fakeworker:
1408 self.fakeworker[mc] = self._start_worker(mc, True, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001409
1410 def teardown_workers(self):
1411 self.teardown = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001412 for mc in self.worker:
1413 self._teardown_worker(self.worker[mc])
1414 self.worker = {}
1415 for mc in self.fakeworker:
1416 self._teardown_worker(self.fakeworker[mc])
1417 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001418
1419 def read_workers(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001420 for mc in self.worker:
1421 self.worker[mc].pipe.read()
1422 for mc in self.fakeworker:
1423 self.fakeworker[mc].pipe.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001424
1425 def active_fds(self):
1426 fds = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001427 for mc in self.worker:
1428 fds.append(self.worker[mc].pipe.input)
1429 for mc in self.fakeworker:
1430 fds.append(self.fakeworker[mc].pipe.input)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001431 return fds
1432
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001433 def check_stamp_task(self, tid, taskname = None, recurse = False, cache = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001434 def get_timestamp(f):
1435 try:
1436 if not os.access(f, os.F_OK):
1437 return None
1438 return os.stat(f)[stat.ST_MTIME]
1439 except:
1440 return None
1441
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001442 (mc, fn, tn, taskfn) = split_tid_mcfn(tid)
1443 if taskname is None:
1444 taskname = tn
1445
Andrew Geissler517393d2023-01-13 08:55:19 -06001446 stampfile = bb.parse.siggen.stampfile_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001447
1448 # If the stamp is missing, it's not current
1449 if not os.access(stampfile, os.F_OK):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001450 logger.debug2("Stampfile %s not available", stampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001451 return False
1452 # If it's a 'nostamp' task, it's not current
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001453 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001454 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001455 logger.debug2("%s.%s is nostamp\n", fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001456 return False
1457
Andrew Geissler517393d2023-01-13 08:55:19 -06001458 if taskname.endswith("_setscene"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001459 return True
1460
1461 if cache is None:
1462 cache = {}
1463
1464 iscurrent = True
1465 t1 = get_timestamp(stampfile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001466 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001467 if iscurrent:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001468 (mc2, fn2, taskname2, taskfn2) = split_tid_mcfn(dep)
Andrew Geissler517393d2023-01-13 08:55:19 -06001469 stampfile2 = bb.parse.siggen.stampfile_mcfn(taskname2, taskfn2)
1470 stampfile3 = bb.parse.siggen.stampfile_mcfn(taskname2 + "_setscene", taskfn2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001471 t2 = get_timestamp(stampfile2)
1472 t3 = get_timestamp(stampfile3)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001473 if t3 and not t2:
1474 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001475 if t3 and t3 > t2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001476 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00001477 if fn == fn2:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001478 if not t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001479 logger.debug2('Stampfile %s does not exist', stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001480 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001481 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001482 if t1 < t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001483 logger.debug2('Stampfile %s < %s', stampfile, stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001484 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001485 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001486 if recurse and iscurrent:
1487 if dep in cache:
1488 iscurrent = cache[dep]
1489 if not iscurrent:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001490 logger.debug2('Stampfile for dependency %s:%s invalid (cached)' % (fn2, taskname2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001491 else:
1492 iscurrent = self.check_stamp_task(dep, recurse=True, cache=cache)
1493 cache[dep] = iscurrent
1494 if recurse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001495 cache[tid] = iscurrent
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001496 return iscurrent
1497
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001498 def validate_hashes(self, tocheck, data, currentcount=0, siginfo=False, summary=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04001499 valid = set()
1500 if self.hashvalidate:
Brad Bishop08902b02019-08-20 09:16:51 -04001501 sq_data = {}
1502 sq_data['hash'] = {}
1503 sq_data['hashfn'] = {}
1504 sq_data['unihash'] = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04001505 for tid in tocheck:
1506 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04001507 sq_data['hash'][tid] = self.rqdata.runtaskentries[tid].hash
1508 sq_data['hashfn'][tid] = self.rqdata.dataCaches[mc].hashfn[taskfn]
1509 sq_data['unihash'][tid] = self.rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -04001510
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001511 valid = self.validate_hash(sq_data, data, siginfo, currentcount, summary)
Brad Bishop96ff1982019-08-19 13:50:42 -04001512
1513 return valid
1514
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001515 def validate_hash(self, sq_data, d, siginfo, currentcount, summary):
1516 locs = {"sq_data" : sq_data, "d" : d, "siginfo" : siginfo, "currentcount" : currentcount, "summary" : summary}
Brad Bishop19323692019-04-05 15:28:33 -04001517
Brad Bishop08902b02019-08-20 09:16:51 -04001518 # Metadata has **kwargs so args can be added, sq_data can also gain new fields
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001519 call = self.hashvalidate + "(sq_data, d, siginfo=siginfo, currentcount=currentcount, summary=summary)"
Brad Bishop19323692019-04-05 15:28:33 -04001520
Brad Bishop19323692019-04-05 15:28:33 -04001521 return bb.utils.better_eval(call, locs)
1522
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001523 def _execute_runqueue(self):
1524 """
1525 Run the tasks in a queue prepared by rqdata.prepare()
1526 Upon failure, optionally try to recover the build using any alternate providers
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001527 (if the halt on failure configuration option isn't set)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001528 """
1529
1530 retval = True
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001531 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001532
1533 if self.state is runQueuePrepare:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001534 # NOTE: if you add, remove or significantly refactor the stages of this
1535 # process then you should recalculate the weightings here. This is quite
1536 # easy to do - just change the next line temporarily to pass debug=True as
1537 # the last parameter and you'll get a printout of the weightings as well
1538 # as a map to the lines where next_stage() was called. Of course this isn't
1539 # critical, but it helps to keep the progress reporting accurate.
1540 self.rqdata.init_progress_reporter = bb.progress.MultiStageProcessProgressReporter(self.cooker.data,
1541 "Initialising tasks",
1542 [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 -05001543 if self.rqdata.prepare() == 0:
1544 self.state = runQueueComplete
1545 else:
1546 self.state = runQueueSceneInit
Brad Bishop00e122a2019-10-05 11:10:57 -04001547 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001548
1549 if self.state is runQueueSceneInit:
Brad Bishop96ff1982019-08-19 13:50:42 -04001550 self.rqdata.init_progress_reporter.next_stage()
1551
1552 # we are ready to run, emit dependency info to any UI or class which
1553 # needs it
1554 depgraph = self.cooker.buildDependTree(self, self.rqdata.taskData)
1555 self.rqdata.init_progress_reporter.next_stage()
1556 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
1557
Brad Bishope2d5b612018-11-23 10:55:50 +13001558 if not self.dm_event_handler_registered:
1559 res = bb.event.register(self.dm_event_handler_name,
Andrew Geissler517393d2023-01-13 08:55:19 -06001560 lambda x, y: self.dm.check(self) if self.state in [runQueueRunning, runQueueCleanUp] else False,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001561 ('bb.event.HeartbeatEvent',), data=self.cfgData)
Brad Bishope2d5b612018-11-23 10:55:50 +13001562 self.dm_event_handler_registered = True
1563
Patrick Williams169d7bc2024-01-05 11:33:25 -06001564 self.rqdata.init_progress_reporter.next_stage()
1565 self.rqexe = RunQueueExecute(self)
1566
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001567 dump = self.cooker.configuration.dump_signatures
1568 if dump:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001569 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001570 if 'printdiff' in dump:
1571 invalidtasks = self.print_diffscenetasks()
1572 self.dump_signatures(dump)
1573 if 'printdiff' in dump:
1574 self.write_diffscenetasks(invalidtasks)
1575 self.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001576
Brad Bishop96ff1982019-08-19 13:50:42 -04001577 if self.state is runQueueSceneInit:
Patrick Williams169d7bc2024-01-05 11:33:25 -06001578 self.start_worker(self.rqexe)
1579 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001580
Brad Bishop96ff1982019-08-19 13:50:42 -04001581 # If we don't have any setscene functions, skip execution
Andrew Geissler595f6302022-01-24 19:11:47 +00001582 if not self.rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04001583 logger.info('No setscene tasks')
1584 for tid in self.rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00001585 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04001586 self.rqexe.setbuildable(tid)
1587 self.rqexe.tasks_notcovered.add(tid)
1588 self.rqexe.sqdone = True
1589 logger.info('Executing Tasks')
1590 self.state = runQueueRunning
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001591
1592 if self.state is runQueueRunning:
1593 retval = self.rqexe.execute()
1594
1595 if self.state is runQueueCleanUp:
1596 retval = self.rqexe.finish()
1597
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001598 build_done = self.state is runQueueComplete or self.state is runQueueFailed
1599
1600 if build_done and self.dm_event_handler_registered:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001601 bb.event.remove(self.dm_event_handler_name, None, data=self.cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001602 self.dm_event_handler_registered = False
1603
1604 if build_done and self.rqexe:
Brad Bishop08902b02019-08-20 09:16:51 -04001605 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001606 self.teardown_workers()
Brad Bishop96ff1982019-08-19 13:50:42 -04001607 if self.rqexe:
1608 if self.rqexe.stats.failed:
1609 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)
1610 else:
1611 # Let's avoid the word "failed" if nothing actually did
1612 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 -05001613
1614 if self.state is runQueueFailed:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001615 raise bb.runqueue.TaskFailure(self.rqexe.failed_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001616
1617 if self.state is runQueueComplete:
1618 # All done
1619 return False
1620
1621 # Loop
1622 return retval
1623
1624 def execute_runqueue(self):
1625 # Catch unexpected exceptions and ensure we exit when an error occurs, not loop.
1626 try:
1627 return self._execute_runqueue()
1628 except bb.runqueue.TaskFailure:
1629 raise
1630 except SystemExit:
1631 raise
1632 except bb.BBHandledException:
1633 try:
1634 self.teardown_workers()
1635 except:
1636 pass
1637 self.state = runQueueComplete
1638 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001639 except Exception as err:
1640 logger.exception("An uncaught exception occurred in runqueue")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001641 try:
1642 self.teardown_workers()
1643 except:
1644 pass
1645 self.state = runQueueComplete
1646 raise
1647
1648 def finish_runqueue(self, now = False):
1649 if not self.rqexe:
1650 self.state = runQueueComplete
1651 return
1652
1653 if now:
1654 self.rqexe.finish_now()
1655 else:
1656 self.rqexe.finish()
1657
Andrew Geissler517393d2023-01-13 08:55:19 -06001658 def _rq_dump_sigtid(self, tids):
1659 for tid in tids:
1660 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1661 dataCaches = self.rqdata.dataCaches
1662 bb.parse.siggen.dump_sigtask(taskfn, taskname, dataCaches[mc].stamp[taskfn], True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001663
1664 def dump_signatures(self, options):
Andrew Geissler517393d2023-01-13 08:55:19 -06001665 if bb.cooker.CookerFeatures.RECIPE_SIGGEN_INFO not in self.cooker.featureset:
1666 bb.fatal("The dump signatures functionality needs the RECIPE_SIGGEN_INFO feature enabled")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001667
Andrew Geissler517393d2023-01-13 08:55:19 -06001668 bb.note("Writing task signature files")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001669
1670 max_process = int(self.cfgData.getVar("BB_NUMBER_PARSE_THREADS") or os.cpu_count() or 1)
Andrew Geissler517393d2023-01-13 08:55:19 -06001671 def chunkify(l, n):
1672 return [l[i::n] for i in range(n)]
1673 tids = chunkify(list(self.rqdata.runtaskentries), max_process)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001674 # We cannot use the real multiprocessing.Pool easily due to some local data
1675 # that can't be pickled. This is a cheap multi-process solution.
1676 launched = []
Andrew Geissler517393d2023-01-13 08:55:19 -06001677 while tids:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001678 if len(launched) < max_process:
Andrew Geissler517393d2023-01-13 08:55:19 -06001679 p = Process(target=self._rq_dump_sigtid, args=(tids.pop(), ))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001680 p.start()
1681 launched.append(p)
1682 for q in launched:
1683 # The finished processes are joined when calling is_alive()
1684 if not q.is_alive():
1685 launched.remove(q)
1686 for p in launched:
1687 p.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001688
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001689 bb.parse.siggen.dump_sigs(self.rqdata.dataCaches, options)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001690
1691 return
1692
1693 def print_diffscenetasks(self):
Patrick Williams705982a2024-01-12 09:51:57 -06001694 def get_root_invalid_tasks(task, taskdepends, valid, noexec, visited_invalid):
1695 invalidtasks = []
1696 for t in taskdepends[task].depends:
1697 if t not in valid and t not in visited_invalid:
1698 invalidtasks.extend(get_root_invalid_tasks(t, taskdepends, valid, noexec, visited_invalid))
1699 visited_invalid.add(t)
1700
1701 direct_invalid = [t for t in taskdepends[task].depends if t not in valid]
1702 if not direct_invalid and task not in noexec:
1703 invalidtasks = [task]
1704 return invalidtasks
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001705
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001706 noexec = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001707 tocheck = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001708
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001709 for tid in self.rqdata.runtaskentries:
1710 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1711 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001712
1713 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001714 noexec.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001715 continue
1716
Brad Bishop96ff1982019-08-19 13:50:42 -04001717 tocheck.add(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001718
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001719 valid_new = self.validate_hashes(tocheck, self.cooker.data, 0, True, summary=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001720
1721 # Tasks which are both setscene and noexec never care about dependencies
1722 # We therefore find tasks which are setscene and noexec and mark their
1723 # unique dependencies as valid.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001724 for tid in noexec:
1725 if tid not in self.rqdata.runq_setscene_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001726 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001727 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001728 hasnoexecparents = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001729 for dep2 in self.rqdata.runtaskentries[dep].revdeps:
1730 if dep2 in self.rqdata.runq_setscene_tids and dep2 in noexec:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001731 continue
1732 hasnoexecparents = False
1733 break
1734 if hasnoexecparents:
1735 valid_new.add(dep)
1736
1737 invalidtasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001738
Patrick Williams705982a2024-01-12 09:51:57 -06001739 toptasks = set(["{}:{}".format(t[3], t[2]) for t in self.rqdata.targets])
1740 for tid in toptasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001741 toprocess = set([tid])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001742 while toprocess:
1743 next = set()
Patrick Williams705982a2024-01-12 09:51:57 -06001744 visited_invalid = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001745 for t in toprocess:
Patrick Williams705982a2024-01-12 09:51:57 -06001746 if t not in valid_new and t not in noexec:
1747 invalidtasks.update(get_root_invalid_tasks(t, self.rqdata.runtaskentries, valid_new, noexec, visited_invalid))
1748 continue
1749 if t in self.rqdata.runq_setscene_tids:
1750 for dep in self.rqexe.sqdata.sq_deps[t]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001751 next.add(dep)
Patrick Williams705982a2024-01-12 09:51:57 -06001752 continue
1753
1754 for dep in self.rqdata.runtaskentries[t].depends:
1755 next.add(dep)
1756
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001757 toprocess = next
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001758
1759 tasklist = []
Patrick Williams705982a2024-01-12 09:51:57 -06001760 for tid in invalidtasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001761 tasklist.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001762
1763 if tasklist:
1764 bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
1765
Patrick Williams705982a2024-01-12 09:51:57 -06001766 return invalidtasks
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001767
1768 def write_diffscenetasks(self, invalidtasks):
Patrick Williams169d7bc2024-01-05 11:33:25 -06001769 bb.siggen.check_siggen_version(bb.siggen)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001770
1771 # Define recursion callback
1772 def recursecb(key, hash1, hash2):
1773 hashes = [hash1, hash2]
Patrick Williams169d7bc2024-01-05 11:33:25 -06001774 bb.debug(1, "Recursively looking for recipe {} hashes {}".format(key, hashes))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001775 hashfiles = bb.siggen.find_siginfo(key, None, hashes, self.cfgData)
Patrick Williams169d7bc2024-01-05 11:33:25 -06001776 bb.debug(1, "Found hashfiles:\n{}".format(hashfiles))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001777
1778 recout = []
1779 if len(hashfiles) == 2:
Patrick Williams169d7bc2024-01-05 11:33:25 -06001780 out2 = bb.siggen.compare_sigfiles(hashfiles[hash1]['path'], hashfiles[hash2]['path'], recursecb)
Brad Bishopc342db32019-05-15 21:57:59 -04001781 recout.extend(list(' ' + l for l in out2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001782 else:
1783 recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
1784
1785 return recout
1786
1787
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001788 for tid in invalidtasks:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001789 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1790 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001791 h = self.rqdata.runtaskentries[tid].unihash
Patrick Williams169d7bc2024-01-05 11:33:25 -06001792 bb.debug(1, "Looking for recipe {} task {}".format(pn, taskname))
Patrick Williams03907ee2022-05-01 06:28:52 -05001793 matches = bb.siggen.find_siginfo(pn, taskname, [], self.cooker.databuilder.mcdata[mc])
Patrick Williams169d7bc2024-01-05 11:33:25 -06001794 bb.debug(1, "Found hashfiles:\n{}".format(matches))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001795 match = None
Patrick Williams169d7bc2024-01-05 11:33:25 -06001796 for m in matches.values():
1797 if h in m['path']:
1798 match = m['path']
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001799 if match is None:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001800 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 -06001801 matches = {k : v for k, v in iter(matches.items()) if h not in k}
Patrick Williams169d7bc2024-01-05 11:33:25 -06001802 matches_local = {k : v for k, v in iter(matches.items()) if h not in k and not v['sstate']}
1803 if matches_local:
1804 matches = matches_local
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001805 if matches:
Patrick Williams169d7bc2024-01-05 11:33:25 -06001806 latestmatch = matches[sorted(matches.keys(), key=lambda h: matches[h]['time'])[-1]]['path']
Brad Bishop19323692019-04-05 15:28:33 -04001807 prevh = __find_sha256__.search(latestmatch).group(0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001808 output = bb.siggen.compare_sigfiles(latestmatch, match, recursecb)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001809 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 -05001810
Brad Bishop96ff1982019-08-19 13:50:42 -04001811
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001812class RunQueueExecute:
1813
1814 def __init__(self, rq):
1815 self.rq = rq
1816 self.cooker = rq.cooker
1817 self.cfgData = rq.cfgData
1818 self.rqdata = rq.rqdata
1819
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001820 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
1821 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001822 self.max_cpu_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_CPU")
1823 self.max_io_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_IO")
Patrick Williams92b42cb2022-09-03 06:53:57 -05001824 self.max_memory_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_MEMORY")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001825
Brad Bishop96ff1982019-08-19 13:50:42 -04001826 self.sq_buildable = set()
1827 self.sq_running = set()
1828 self.sq_live = set()
1829
Brad Bishop08902b02019-08-20 09:16:51 -04001830 self.updated_taskhash_queue = []
1831 self.pending_migrations = set()
1832
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001833 self.runq_buildable = set()
1834 self.runq_running = set()
1835 self.runq_complete = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001836 self.runq_tasksrun = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001837
1838 self.build_stamps = {}
1839 self.build_stamps2 = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001840 self.failed_tids = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001841 self.sq_deferred = {}
Patrick Williams169d7bc2024-01-05 11:33:25 -06001842 self.sq_needed_harddeps = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001843
1844 self.stampcache = {}
1845
Brad Bishop08902b02019-08-20 09:16:51 -04001846 self.holdoff_tasks = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04001847 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04001848 self.sqdone = False
1849
Andrew Geissler5199d832021-09-24 16:47:35 -05001850 self.stats = RunQueueStats(len(self.rqdata.runtaskentries), len(self.rqdata.runq_setscene_tids))
Brad Bishop96ff1982019-08-19 13:50:42 -04001851
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001852 if self.number_tasks <= 0:
1853 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks)
1854
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001855 lower_limit = 1.0
1856 upper_limit = 1000000.0
1857 if self.max_cpu_pressure:
1858 self.max_cpu_pressure = float(self.max_cpu_pressure)
1859 if self.max_cpu_pressure < lower_limit:
1860 bb.fatal("Invalid BB_PRESSURE_MAX_CPU %s, minimum value is %s." % (self.max_cpu_pressure, lower_limit))
1861 if self.max_cpu_pressure > upper_limit:
1862 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))
1863
1864 if self.max_io_pressure:
1865 self.max_io_pressure = float(self.max_io_pressure)
1866 if self.max_io_pressure < lower_limit:
1867 bb.fatal("Invalid BB_PRESSURE_MAX_IO %s, minimum value is %s." % (self.max_io_pressure, lower_limit))
1868 if self.max_io_pressure > upper_limit:
1869 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))
1870
Patrick Williams92b42cb2022-09-03 06:53:57 -05001871 if self.max_memory_pressure:
1872 self.max_memory_pressure = float(self.max_memory_pressure)
1873 if self.max_memory_pressure < lower_limit:
1874 bb.fatal("Invalid BB_PRESSURE_MAX_MEMORY %s, minimum value is %s." % (self.max_memory_pressure, lower_limit))
1875 if self.max_memory_pressure > upper_limit:
1876 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))
1877
Brad Bishop96ff1982019-08-19 13:50:42 -04001878 # List of setscene tasks which we've covered
1879 self.scenequeue_covered = set()
1880 # List of tasks which are covered (including setscene ones)
1881 self.tasks_covered = set()
1882 self.tasks_scenequeue_done = set()
1883 self.scenequeue_notcovered = set()
1884 self.tasks_notcovered = set()
1885 self.scenequeue_notneeded = set()
1886
Brad Bishop96ff1982019-08-19 13:50:42 -04001887 schedulers = self.get_schedulers()
1888 for scheduler in schedulers:
1889 if self.scheduler == scheduler.name:
1890 self.sched = scheduler(self, self.rqdata)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001891 logger.debug("Using runqueue scheduler '%s'", scheduler.name)
Brad Bishop96ff1982019-08-19 13:50:42 -04001892 break
1893 else:
1894 bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
1895 (self.scheduler, ", ".join(obj.name for obj in schedulers)))
1896
Andrew Geissler595f6302022-01-24 19:11:47 +00001897 #if self.rqdata.runq_setscene_tids:
Brad Bishop08902b02019-08-20 09:16:51 -04001898 self.sqdata = SQData()
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001899 build_scenequeue_data(self.sqdata, self.rqdata, self)
1900
1901 update_scenequeue_data(self.sqdata.sq_revdeps, self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=True)
1902
1903 # Compute a list of 'stale' sstate tasks where the current hash does not match the one
1904 # in any stamp files. Pass the list out to metadata as an event.
1905 found = {}
1906 for tid in self.rqdata.runq_setscene_tids:
1907 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1908 stamps = bb.build.find_stale_stamps(taskname, taskfn)
1909 if stamps:
1910 if mc not in found:
1911 found[mc] = {}
1912 found[mc][tid] = stamps
1913 for mc in found:
1914 event = bb.event.StaleSetSceneTasks(found[mc])
1915 bb.event.fire(event, self.cooker.databuilder.mcdata[mc])
Brad Bishop96ff1982019-08-19 13:50:42 -04001916
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001917 def runqueue_process_waitpid(self, task, status, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001918
1919 # self.build_stamps[pid] may not exist when use shared work directory.
1920 if task in self.build_stamps:
1921 self.build_stamps2.remove(self.build_stamps[task])
1922 del self.build_stamps[task]
1923
Brad Bishop96ff1982019-08-19 13:50:42 -04001924 if task in self.sq_live:
1925 if status != 0:
1926 self.sq_task_fail(task, status)
1927 else:
1928 self.sq_task_complete(task)
1929 self.sq_live.remove(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05001930 self.stats.updateActiveSetscene(len(self.sq_live))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001931 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04001932 if status != 0:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001933 self.task_fail(task, status, fakerootlog=fakerootlog)
Brad Bishop96ff1982019-08-19 13:50:42 -04001934 else:
1935 self.task_complete(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001936 return True
1937
1938 def finish_now(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001939 for mc in self.rq.worker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001940 try:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001941 RunQueue.send_pickled_data(self.rq.worker[mc].process, b"", "finishnow")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001942 self.rq.worker[mc].process.stdin.flush()
1943 except IOError:
1944 # worker must have died?
1945 pass
1946 for mc in self.rq.fakeworker:
1947 try:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001948 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, b"", "finishnow")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001949 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001950 except IOError:
1951 # worker must have died?
1952 pass
1953
Andrew Geissler595f6302022-01-24 19:11:47 +00001954 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001955 self.rq.state = runQueueFailed
1956 return
1957
1958 self.rq.state = runQueueComplete
1959 return
1960
1961 def finish(self):
1962 self.rq.state = runQueueCleanUp
1963
Andrew Geissler5199d832021-09-24 16:47:35 -05001964 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001965 if active > 0:
1966 bb.event.fire(runQueueExitWait(active), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001967 self.rq.read_workers()
1968 return self.rq.active_fds()
1969
Andrew Geissler595f6302022-01-24 19:11:47 +00001970 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001971 self.rq.state = runQueueFailed
1972 return True
1973
1974 self.rq.state = runQueueComplete
1975 return True
1976
Brad Bishop96ff1982019-08-19 13:50:42 -04001977 # Used by setscene only
1978 def check_dependencies(self, task, taskdeps):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001979 if not self.rq.depvalidate:
1980 return False
1981
Brad Bishop08902b02019-08-20 09:16:51 -04001982 # Must not edit parent data
1983 taskdeps = set(taskdeps)
1984
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001985 taskdata = {}
1986 taskdeps.add(task)
1987 for dep in taskdeps:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001988 (mc, fn, taskname, taskfn) = split_tid_mcfn(dep)
1989 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001990 taskdata[dep] = [pn, taskname, fn]
1991 call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001992 locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001993 valid = bb.utils.better_eval(call, locs)
1994 return valid
1995
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001996 def can_start_task(self):
Andrew Geissler5199d832021-09-24 16:47:35 -05001997 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001998 can_start = active < self.number_tasks
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001999 return can_start
2000
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002001 def get_schedulers(self):
2002 schedulers = set(obj for obj in globals().values()
2003 if type(obj) is type and
2004 issubclass(obj, RunQueueScheduler))
2005
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002006 user_schedulers = self.cfgData.getVar("BB_SCHEDULERS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002007 if user_schedulers:
2008 for sched in user_schedulers.split():
2009 if not "." in sched:
2010 bb.note("Ignoring scheduler '%s' from BB_SCHEDULERS: not an import" % sched)
2011 continue
2012
2013 modname, name = sched.rsplit(".", 1)
2014 try:
2015 module = __import__(modname, fromlist=(name,))
2016 except ImportError as exc:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06002017 bb.fatal("Unable to import scheduler '%s' from '%s': %s" % (name, modname, exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002018 else:
2019 schedulers.add(getattr(module, name))
2020 return schedulers
2021
2022 def setbuildable(self, task):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002023 self.runq_buildable.add(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04002024 self.sched.newbuildable(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002025
2026 def task_completeoutright(self, task):
2027 """
2028 Mark a task as completed
2029 Look at the reverse dependencies and mark any task with
2030 completed dependencies as buildable
2031 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002032 self.runq_complete.add(task)
2033 for revdep in self.rqdata.runtaskentries[task].revdeps:
2034 if revdep in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002035 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002036 if revdep in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002037 continue
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002038 alldeps = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002039 for dep in self.rqdata.runtaskentries[revdep].depends:
2040 if dep not in self.runq_complete:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002041 alldeps = False
2042 break
2043 if alldeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002044 self.setbuildable(revdep)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002045 logger.debug("Marking task %s as buildable", revdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002046
Andrew Geissler8f840682023-07-21 09:09:43 -05002047 found = None
2048 for t in sorted(self.sq_deferred.copy()):
Andrew Geissler5199d832021-09-24 16:47:35 -05002049 if self.sq_deferred[t] == task:
Andrew Geissler8f840682023-07-21 09:09:43 -05002050 # Allow the next deferred task to run. Any other deferred tasks should be deferred after that task.
2051 # We shouldn't allow all to run at once as it is prone to races.
2052 if not found:
2053 bb.debug(1, "Deferred task %s now buildable" % t)
2054 del self.sq_deferred[t]
2055 update_scenequeue_data([t], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
2056 found = t
2057 else:
2058 bb.debug(1, "Deferring %s after %s" % (t, found))
2059 self.sq_deferred[t] = found
Andrew Geissler5199d832021-09-24 16:47:35 -05002060
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002061 def task_complete(self, task):
2062 self.stats.taskCompleted()
2063 bb.event.fire(runQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
2064 self.task_completeoutright(task)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002065 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002066
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002067 def task_fail(self, task, exitcode, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002068 """
2069 Called when a task has failed
2070 Updates the state engine with the failure
2071 """
2072 self.stats.taskFailed()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002073 self.failed_tids.append(task)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002074
Andrew Geissler595f6302022-01-24 19:11:47 +00002075 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002076 if fakerootlog and os.path.exists(fakerootlog):
2077 with open(fakerootlog) as fakeroot_log_file:
2078 fakeroot_failed = False
2079 for line in reversed(fakeroot_log_file.readlines()):
2080 for fakeroot_error in ['mismatch', 'error', 'fatal']:
2081 if fakeroot_error in line.lower():
2082 fakeroot_failed = True
2083 if 'doing new pid setup and server start' in line:
2084 break
Andrew Geissler595f6302022-01-24 19:11:47 +00002085 fakeroot_log.append(line)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002086
2087 if not fakeroot_failed:
Andrew Geissler595f6302022-01-24 19:11:47 +00002088 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002089
Andrew Geissler595f6302022-01-24 19:11:47 +00002090 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 -05002091
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002092 if self.rqdata.taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002093 self.rq.state = runQueueCleanUp
2094
2095 def task_skip(self, task, reason):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002096 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002097 self.setbuildable(task)
2098 bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
2099 self.task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002100 self.stats.taskSkipped()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002101 self.stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002102
Brad Bishop08902b02019-08-20 09:16:51 -04002103 def summarise_scenequeue_errors(self):
2104 err = False
2105 if not self.sqdone:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002106 logger.debug('We could skip tasks %s', "\n".join(sorted(self.scenequeue_covered)))
Andrew Geissler5199d832021-09-24 16:47:35 -05002107 completeevent = sceneQueueComplete(self.stats, self.rq)
Brad Bishop08902b02019-08-20 09:16:51 -04002108 bb.event.fire(completeevent, self.cfgData)
2109 if self.sq_deferred:
2110 logger.error("Scenequeue had deferred entries: %s" % pprint.pformat(self.sq_deferred))
2111 err = True
2112 if self.updated_taskhash_queue:
2113 logger.error("Scenequeue had unprocessed changed taskhash entries: %s" % pprint.pformat(self.updated_taskhash_queue))
2114 err = True
2115 if self.holdoff_tasks:
2116 logger.error("Scenequeue had holdoff tasks: %s" % pprint.pformat(self.holdoff_tasks))
2117 err = True
2118
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002119 for tid in self.scenequeue_covered.intersection(self.scenequeue_notcovered):
2120 # No task should end up in both covered and uncovered, that is a bug.
2121 logger.error("Setscene task %s in both covered and notcovered." % tid)
2122
Brad Bishop08902b02019-08-20 09:16:51 -04002123 for tid in self.rqdata.runq_setscene_tids:
2124 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2125 err = True
2126 logger.error("Setscene Task %s was never marked as covered or not covered" % tid)
2127 if tid not in self.sq_buildable:
2128 err = True
2129 logger.error("Setscene Task %s was never marked as buildable" % tid)
2130 if tid not in self.sq_running:
2131 err = True
2132 logger.error("Setscene Task %s was never marked as running" % tid)
2133
2134 for x in self.rqdata.runtaskentries:
2135 if x not in self.tasks_covered and x not in self.tasks_notcovered:
2136 logger.error("Task %s was never moved from the setscene queue" % x)
2137 err = True
2138 if x not in self.tasks_scenequeue_done:
2139 logger.error("Task %s was never processed by the setscene code" % x)
2140 err = True
Andrew Geissler595f6302022-01-24 19:11:47 +00002141 if not self.rqdata.runtaskentries[x].depends and x not in self.runq_buildable:
Brad Bishop08902b02019-08-20 09:16:51 -04002142 logger.error("Task %s was never marked as buildable by the setscene code" % x)
2143 err = True
2144 return err
2145
2146
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002147 def execute(self):
2148 """
Brad Bishop96ff1982019-08-19 13:50:42 -04002149 Run the tasks in a queue prepared by prepare_runqueue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002150 """
2151
2152 self.rq.read_workers()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002153 if self.updated_taskhash_queue or self.pending_migrations:
2154 self.process_possible_migrations()
2155
2156 if not hasattr(self, "sorted_setscene_tids"):
2157 # Don't want to sort this set every execution
2158 self.sorted_setscene_tids = sorted(self.rqdata.runq_setscene_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002159
Brad Bishop96ff1982019-08-19 13:50:42 -04002160 task = None
2161 if not self.sqdone and self.can_start_task():
2162 # Find the next setscene to run
Andrew Geissler82c905d2020-04-13 13:39:40 -05002163 for nexttask in self.sorted_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002164 if nexttask in self.sq_buildable and nexttask not in self.sq_running and self.sqdata.stamps[nexttask] not in self.build_stamps.values():
Patrick Williams169d7bc2024-01-05 11:33:25 -06002165 if nexttask not in self.sqdata.unskippable and self.sqdata.sq_revdeps[nexttask] and \
2166 nexttask not in self.sq_needed_harddeps and \
2167 self.sqdata.sq_revdeps[nexttask].issubset(self.scenequeue_covered) and \
2168 self.check_dependencies(nexttask, self.sqdata.sq_revdeps[nexttask]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002169 if nexttask not in self.rqdata.target_tids:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002170 logger.debug2("Skipping setscene for task %s" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002171 self.sq_task_skip(nexttask)
2172 self.scenequeue_notneeded.add(nexttask)
2173 if nexttask in self.sq_deferred:
2174 del self.sq_deferred[nexttask]
2175 return True
Patrick Williams169d7bc2024-01-05 11:33:25 -06002176 if nexttask in self.sqdata.sq_harddeps_rev and not self.sqdata.sq_harddeps_rev[nexttask].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2177 logger.debug2("Deferring %s due to hard dependencies" % nexttask)
2178 updated = False
2179 for dep in self.sqdata.sq_harddeps_rev[nexttask]:
2180 if dep not in self.sq_needed_harddeps:
2181 logger.debug2("Enabling task %s as it is a hard dependency" % dep)
2182 self.sq_buildable.add(dep)
2183 self.sq_needed_harddeps.add(dep)
2184 updated = True
2185 if updated:
2186 return True
2187 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002188 # If covered tasks are running, need to wait for them to complete
2189 for t in self.sqdata.sq_covered_tasks[nexttask]:
2190 if t in self.runq_running and t not in self.runq_complete:
2191 continue
Brad Bishop96ff1982019-08-19 13:50:42 -04002192 if nexttask in self.sq_deferred:
2193 if self.sq_deferred[nexttask] not in self.runq_complete:
2194 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002195 logger.debug("Task %s no longer deferred" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002196 del self.sq_deferred[nexttask]
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002197 valid = self.rq.validate_hashes(set([nexttask]), self.cooker.data, 0, False, summary=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002198 if not valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002199 logger.debug("%s didn't become valid, skipping setscene" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002200 self.sq_task_failoutright(nexttask)
2201 return True
Brad Bishop96ff1982019-08-19 13:50:42 -04002202 if nexttask in self.sqdata.outrightfail:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002203 logger.debug2('No package found, so skipping setscene task %s', nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002204 self.sq_task_failoutright(nexttask)
2205 return True
2206 if nexttask in self.sqdata.unskippable:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002207 logger.debug2("Setscene task %s is unskippable" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002208 task = nexttask
2209 break
2210 if task is not None:
2211 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
2212 taskname = taskname + "_setscene"
2213 if self.rq.check_stamp_task(task, taskname_from_tid(task), recurse = True, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002214 logger.debug2('Stamp for underlying task %s is current, so skipping setscene variant', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002215 self.sq_task_failoutright(task)
2216 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002217
Brad Bishop96ff1982019-08-19 13:50:42 -04002218 if self.cooker.configuration.force:
2219 if task in self.rqdata.target_tids:
2220 self.sq_task_failoutright(task)
2221 return True
2222
2223 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002224 logger.debug2('Setscene stamp current task %s, so skip it and its dependencies', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002225 self.sq_task_skip(task)
2226 return True
2227
2228 if self.cooker.configuration.skipsetscene:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002229 logger.debug2('No setscene tasks should be executed. Skipping %s', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002230 self.sq_task_failoutright(task)
2231 return True
2232
Andrew Geissler5199d832021-09-24 16:47:35 -05002233 startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
Brad Bishop96ff1982019-08-19 13:50:42 -04002234 bb.event.fire(startevent, self.cfgData)
2235
Brad Bishop96ff1982019-08-19 13:50:42 -04002236 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002237 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002238 runtask = {
2239 'fn' : taskfn,
2240 'task' : task,
2241 'taskname' : taskname,
2242 'taskhash' : self.rqdata.get_task_hash(task),
2243 'unihash' : self.rqdata.get_task_unihash(task),
2244 'quieterrors' : True,
2245 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002246 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002247 'taskdepdata' : self.sq_build_taskdepdata(task),
2248 'dry_run' : False,
2249 'taskdep': taskdep,
2250 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2251 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2252 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2253 }
2254
Brad Bishop96ff1982019-08-19 13:50:42 -04002255 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
2256 if not mc in self.rq.fakeworker:
2257 self.rq.start_fakeworker(self, mc)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002258 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, runtask, "runtask")
Brad Bishop96ff1982019-08-19 13:50:42 -04002259 self.rq.fakeworker[mc].process.stdin.flush()
2260 else:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002261 RunQueue.send_pickled_data(self.rq.worker[mc].process, runtask, "runtask")
Brad Bishop96ff1982019-08-19 13:50:42 -04002262 self.rq.worker[mc].process.stdin.flush()
2263
Andrew Geissler517393d2023-01-13 08:55:19 -06002264 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002265 self.build_stamps2.append(self.build_stamps[task])
2266 self.sq_running.add(task)
2267 self.sq_live.add(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05002268 self.stats.updateActiveSetscene(len(self.sq_live))
Brad Bishop96ff1982019-08-19 13:50:42 -04002269 if self.can_start_task():
2270 return True
2271
Brad Bishopc68388fc2019-08-26 01:33:31 -04002272 self.update_holdofftasks()
2273
Brad Bishop08902b02019-08-20 09:16:51 -04002274 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 -05002275 hashequiv_logger.verbose("Setscene tasks completed")
Brad Bishop96ff1982019-08-19 13:50:42 -04002276
Brad Bishop08902b02019-08-20 09:16:51 -04002277 err = self.summarise_scenequeue_errors()
Brad Bishop96ff1982019-08-19 13:50:42 -04002278 if err:
2279 self.rq.state = runQueueFailed
2280 return True
2281
2282 if self.cooker.configuration.setsceneonly:
2283 self.rq.state = runQueueComplete
2284 return True
2285 self.sqdone = True
2286
2287 if self.stats.total == 0:
2288 # nothing to do
2289 self.rq.state = runQueueComplete
2290 return True
2291
2292 if self.cooker.configuration.setsceneonly:
2293 task = None
2294 else:
2295 task = self.sched.next()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002296 if task is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002297 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002298
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002299 if self.rqdata.setscene_ignore_tasks is not None:
2300 if self.check_setscene_ignore_tasks(task):
2301 self.task_fail(task, "setscene ignore_tasks")
Brad Bishop96ff1982019-08-19 13:50:42 -04002302 return True
2303
2304 if task in self.tasks_covered:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002305 logger.debug2("Setscene covered task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002306 self.task_skip(task, "covered")
2307 return True
2308
2309 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002310 logger.debug2("Stamp current task %s", task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002311
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002312 self.task_skip(task, "existing")
Andrew Geissler82c905d2020-04-13 13:39:40 -05002313 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002314 return True
2315
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002316 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002317 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2318 startevent = runQueueTaskStarted(task, self.stats, self.rq,
2319 noexec=True)
2320 bb.event.fire(startevent, self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002321 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002322 self.stats.taskActive()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002323 if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Andrew Geissler517393d2023-01-13 08:55:19 -06002324 bb.build.make_stamp_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002325 self.task_complete(task)
2326 return True
2327 else:
2328 startevent = runQueueTaskStarted(task, self.stats, self.rq)
2329 bb.event.fire(startevent, self.cfgData)
2330
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002331 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002332 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002333 runtask = {
2334 'fn' : taskfn,
2335 'task' : task,
2336 'taskname' : taskname,
2337 'taskhash' : self.rqdata.get_task_hash(task),
2338 'unihash' : self.rqdata.get_task_unihash(task),
2339 'quieterrors' : False,
2340 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002341 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002342 'taskdepdata' : self.build_taskdepdata(task),
2343 'dry_run' : self.rqdata.setscene_enforce,
2344 'taskdep': taskdep,
2345 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2346 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2347 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2348 }
2349
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002350 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 -05002351 if not mc in self.rq.fakeworker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002352 try:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002353 self.rq.start_fakeworker(self, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002354 except OSError as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002355 logger.critical("Failed to spawn fakeroot worker to run %s: %s" % (task, str(exc)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002356 self.rq.state = runQueueFailed
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002357 self.stats.taskFailed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002358 return True
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002359 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, runtask, "runtask")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002360 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002361 else:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002362 RunQueue.send_pickled_data(self.rq.worker[mc].process, runtask, "runtask")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002363 self.rq.worker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002364
Andrew Geissler517393d2023-01-13 08:55:19 -06002365 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002366 self.build_stamps2.append(self.build_stamps[task])
2367 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002368 self.stats.taskActive()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002369 if self.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002370 return True
2371
Andrew Geissler595f6302022-01-24 19:11:47 +00002372 if self.stats.active > 0 or self.sq_live:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002373 self.rq.read_workers()
2374 return self.rq.active_fds()
2375
Brad Bishop96ff1982019-08-19 13:50:42 -04002376 # No more tasks can be run. If we have deferred setscene tasks we should run them.
2377 if self.sq_deferred:
Patrick Williams92b42cb2022-09-03 06:53:57 -05002378 deferred_tid = list(self.sq_deferred.keys())[0]
2379 blocking_tid = self.sq_deferred.pop(deferred_tid)
Patrick Williams2390b1b2022-11-03 13:47:49 -05002380 logger.warning("Runqueue deadlocked on deferred tasks, forcing task %s blocked by %s" % (deferred_tid, blocking_tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04002381 return True
2382
Andrew Geissler595f6302022-01-24 19:11:47 +00002383 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002384 self.rq.state = runQueueFailed
2385 return True
2386
2387 # Sanity Checks
Brad Bishop08902b02019-08-20 09:16:51 -04002388 err = self.summarise_scenequeue_errors()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002389 for task in self.rqdata.runtaskentries:
2390 if task not in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002391 logger.error("Task %s never buildable!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002392 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002393 elif task not in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002394 logger.error("Task %s never ran!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002395 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002396 elif task not in self.runq_complete:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002397 logger.error("Task %s never completed!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002398 err = True
2399
2400 if err:
2401 self.rq.state = runQueueFailed
2402 else:
2403 self.rq.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002404
2405 return True
2406
Brad Bishopc68388fc2019-08-26 01:33:31 -04002407 def filtermcdeps(self, task, mc, deps):
Andrew Geissler99467da2019-02-25 18:54:23 -06002408 ret = set()
Andrew Geissler99467da2019-02-25 18:54:23 -06002409 for dep in deps:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002410 thismc = mc_from_tid(dep)
2411 if thismc != mc:
Andrew Geissler99467da2019-02-25 18:54:23 -06002412 continue
2413 ret.add(dep)
2414 return ret
2415
Brad Bishopa34c0302019-09-23 22:34:48 -04002416 # We filter out multiconfig dependencies from taskdepdata we pass to the tasks
Andrew Geissler99467da2019-02-25 18:54:23 -06002417 # as most code can't handle them
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002418 def build_taskdepdata(self, task):
2419 taskdepdata = {}
Brad Bishopc68388fc2019-08-26 01:33:31 -04002420 mc = mc_from_tid(task)
Brad Bishop08902b02019-08-20 09:16:51 -04002421 next = self.rqdata.runtaskentries[task].depends.copy()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002422 next.add(task)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002423 next = self.filtermcdeps(task, mc, next)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002424 while next:
2425 additional = []
2426 for revdep in next:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002427 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2428 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2429 deps = self.rqdata.runtaskentries[revdep].depends
2430 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002431 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002432 unihash = self.rqdata.runtaskentries[revdep].unihash
Brad Bishopc68388fc2019-08-26 01:33:31 -04002433 deps = self.filtermcdeps(task, mc, deps)
Patrick Williamsb542dec2023-06-09 01:26:37 -05002434 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2435 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002436 for revdep2 in deps:
2437 if revdep2 not in taskdepdata:
2438 additional.append(revdep2)
2439 next = additional
2440
2441 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2442 return taskdepdata
2443
Brad Bishop08902b02019-08-20 09:16:51 -04002444 def update_holdofftasks(self):
Brad Bishopc68388fc2019-08-26 01:33:31 -04002445
2446 if not self.holdoff_need_update:
2447 return
2448
2449 notcovered = set(self.scenequeue_notcovered)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002450 notcovered |= self.sqdata.cantskip
Brad Bishopc68388fc2019-08-26 01:33:31 -04002451 for tid in self.scenequeue_notcovered:
2452 notcovered |= self.sqdata.sq_covered_tasks[tid]
2453 notcovered |= self.sqdata.unskippable.difference(self.rqdata.runq_setscene_tids)
2454 notcovered.intersection_update(self.tasks_scenequeue_done)
2455
2456 covered = set(self.scenequeue_covered)
2457 for tid in self.scenequeue_covered:
2458 covered |= self.sqdata.sq_covered_tasks[tid]
2459 covered.difference_update(notcovered)
2460 covered.intersection_update(self.tasks_scenequeue_done)
2461
2462 for tid in notcovered | covered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002463 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002464 self.setbuildable(tid)
2465 elif self.rqdata.runtaskentries[tid].depends.issubset(self.runq_complete):
2466 self.setbuildable(tid)
2467
2468 self.tasks_covered = covered
2469 self.tasks_notcovered = notcovered
2470
Brad Bishop08902b02019-08-20 09:16:51 -04002471 self.holdoff_tasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002472
Brad Bishop08902b02019-08-20 09:16:51 -04002473 for tid in self.rqdata.runq_setscene_tids:
2474 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2475 self.holdoff_tasks.add(tid)
2476
2477 for tid in self.holdoff_tasks.copy():
2478 for dep in self.sqdata.sq_covered_tasks[tid]:
2479 if dep not in self.runq_complete:
2480 self.holdoff_tasks.add(dep)
2481
Brad Bishopc68388fc2019-08-26 01:33:31 -04002482 self.holdoff_need_update = False
2483
Brad Bishop08902b02019-08-20 09:16:51 -04002484 def process_possible_migrations(self):
2485
2486 changed = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002487 toprocess = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002488 for tid, unihash in self.updated_taskhash_queue.copy():
2489 if tid in self.runq_running and tid not in self.runq_complete:
2490 continue
2491
2492 self.updated_taskhash_queue.remove((tid, unihash))
2493
2494 if unihash != self.rqdata.runtaskentries[tid].unihash:
Andrew Geisslerc926e172021-05-07 16:11:35 -05002495 # Make sure we rehash any other tasks with the same task hash that we're deferred against.
2496 torehash = [tid]
2497 for deftid in self.sq_deferred:
2498 if self.sq_deferred[deftid] == tid:
2499 torehash.append(deftid)
2500 for hashtid in torehash:
2501 hashequiv_logger.verbose("Task %s unihash changed to %s" % (hashtid, unihash))
2502 self.rqdata.runtaskentries[hashtid].unihash = unihash
2503 bb.parse.siggen.set_unihash(hashtid, unihash)
2504 toprocess.add(hashtid)
Andrew Geissler78b72792022-06-14 06:47:25 -05002505 if torehash:
2506 # Need to save after set_unihash above
2507 bb.parse.siggen.save_unitaskhashes()
Brad Bishop08902b02019-08-20 09:16:51 -04002508
Andrew Geissler82c905d2020-04-13 13:39:40 -05002509 # Work out all tasks which depend upon these
2510 total = set()
2511 next = set()
2512 for p in toprocess:
2513 next |= self.rqdata.runtaskentries[p].revdeps
2514 while next:
2515 current = next.copy()
2516 total = total | next
2517 next = set()
2518 for ntid in current:
2519 next |= self.rqdata.runtaskentries[ntid].revdeps
2520 next.difference_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002521
Andrew Geissler82c905d2020-04-13 13:39:40 -05002522 # Now iterate those tasks in dependency order to regenerate their taskhash/unihash
2523 next = set()
2524 for p in total:
Andrew Geissler595f6302022-01-24 19:11:47 +00002525 if not self.rqdata.runtaskentries[p].depends:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002526 next.add(p)
2527 elif self.rqdata.runtaskentries[p].depends.isdisjoint(total):
2528 next.add(p)
2529
2530 # When an item doesn't have dependencies in total, we can process it. Drop items from total when handled
2531 while next:
2532 current = next.copy()
2533 next = set()
2534 for tid in current:
Andrew Geissler595f6302022-01-24 19:11:47 +00002535 if self.rqdata.runtaskentries[p].depends and not self.rqdata.runtaskentries[tid].depends.isdisjoint(total):
Andrew Geissler82c905d2020-04-13 13:39:40 -05002536 continue
2537 orighash = self.rqdata.runtaskentries[tid].hash
Andrew Geissler517393d2023-01-13 08:55:19 -06002538 newhash = bb.parse.siggen.get_taskhash(tid, self.rqdata.runtaskentries[tid].depends, self.rqdata.dataCaches)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002539 origuni = self.rqdata.runtaskentries[tid].unihash
2540 newuni = bb.parse.siggen.get_unihash(tid)
2541 # FIXME, need to check it can come from sstate at all for determinism?
2542 remapped = False
2543 if newuni == origuni:
2544 # Nothing to do, we match, skip code below
2545 remapped = True
2546 elif tid in self.scenequeue_covered or tid in self.sq_live:
2547 # Already ran this setscene task or it running. Report the new taskhash
2548 bb.parse.siggen.report_unihash_equiv(tid, newhash, origuni, newuni, self.rqdata.dataCaches)
2549 hashequiv_logger.verbose("Already covered setscene for %s so ignoring rehash (remap)" % (tid))
2550 remapped = True
2551
2552 if not remapped:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002553 #logger.debug("Task %s hash changes: %s->%s %s->%s" % (tid, orighash, newhash, origuni, newuni))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002554 self.rqdata.runtaskentries[tid].hash = newhash
2555 self.rqdata.runtaskentries[tid].unihash = newuni
2556 changed.add(tid)
2557
2558 next |= self.rqdata.runtaskentries[tid].revdeps
2559 total.remove(tid)
2560 next.intersection_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002561
2562 if changed:
2563 for mc in self.rq.worker:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002564 RunQueue.send_pickled_data(self.rq.worker[mc].process, bb.parse.siggen.get_taskhashes(), "newtaskhashes")
Brad Bishop08902b02019-08-20 09:16:51 -04002565 for mc in self.rq.fakeworker:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002566 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, bb.parse.siggen.get_taskhashes(), "newtaskhashes")
Brad Bishop08902b02019-08-20 09:16:51 -04002567
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002568 hashequiv_logger.debug(pprint.pformat("Tasks changed:\n%s" % (changed)))
Brad Bishop08902b02019-08-20 09:16:51 -04002569
2570 for tid in changed:
2571 if tid not in self.rqdata.runq_setscene_tids:
2572 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002573 if tid not in self.pending_migrations:
2574 self.pending_migrations.add(tid)
2575
Andrew Geissler82c905d2020-04-13 13:39:40 -05002576 update_tasks = []
Brad Bishop08902b02019-08-20 09:16:51 -04002577 for tid in self.pending_migrations.copy():
Andrew Geissler82c905d2020-04-13 13:39:40 -05002578 if tid in self.runq_running or tid in self.sq_live:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002579 # Too late, task already running, not much we can do now
2580 self.pending_migrations.remove(tid)
2581 continue
2582
Brad Bishop08902b02019-08-20 09:16:51 -04002583 valid = True
2584 # Check no tasks this covers are running
2585 for dep in self.sqdata.sq_covered_tasks[tid]:
2586 if dep in self.runq_running and dep not in self.runq_complete:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002587 hashequiv_logger.debug2("Task %s is running which blocks setscene for %s from running" % (dep, tid))
Brad Bishop08902b02019-08-20 09:16:51 -04002588 valid = False
2589 break
2590 if not valid:
2591 continue
2592
2593 self.pending_migrations.remove(tid)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002594 changed = True
Brad Bishop08902b02019-08-20 09:16:51 -04002595
2596 if tid in self.tasks_scenequeue_done:
2597 self.tasks_scenequeue_done.remove(tid)
2598 for dep in self.sqdata.sq_covered_tasks[tid]:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002599 if dep in self.runq_complete and dep not in self.runq_tasksrun:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002600 bb.error("Task %s marked as completed but now needing to rerun? Halting build." % dep)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002601 self.failed_tids.append(tid)
2602 self.rq.state = runQueueCleanUp
2603 return
2604
Brad Bishop08902b02019-08-20 09:16:51 -04002605 if dep not in self.runq_complete:
2606 if dep in self.tasks_scenequeue_done and dep not in self.sqdata.unskippable:
2607 self.tasks_scenequeue_done.remove(dep)
2608
2609 if tid in self.sq_buildable:
2610 self.sq_buildable.remove(tid)
2611 if tid in self.sq_running:
2612 self.sq_running.remove(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002613 if tid in self.sqdata.outrightfail:
2614 self.sqdata.outrightfail.remove(tid)
2615 if tid in self.scenequeue_notcovered:
2616 self.scenequeue_notcovered.remove(tid)
2617 if tid in self.scenequeue_covered:
2618 self.scenequeue_covered.remove(tid)
2619 if tid in self.scenequeue_notneeded:
2620 self.scenequeue_notneeded.remove(tid)
2621
2622 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2623 self.sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2624
2625 if tid in self.stampcache:
2626 del self.stampcache[tid]
2627
2628 if tid in self.build_stamps:
2629 del self.build_stamps[tid]
2630
2631 update_tasks.append(tid)
2632
2633 update_tasks2 = []
2634 for tid in update_tasks:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002635 harddepfail = False
Patrick Williams169d7bc2024-01-05 11:33:25 -06002636 for t in self.sqdata.sq_harddeps_rev[tid]:
2637 if t in self.scenequeue_notcovered:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002638 harddepfail = True
2639 break
2640 if not harddepfail and self.sqdata.sq_revdeps[tid].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
Brad Bishop08902b02019-08-20 09:16:51 -04002641 if tid not in self.sq_buildable:
2642 self.sq_buildable.add(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +00002643 if not self.sqdata.sq_revdeps[tid]:
Brad Bishop08902b02019-08-20 09:16:51 -04002644 self.sq_buildable.add(tid)
2645
Andrew Geissler517393d2023-01-13 08:55:19 -06002646 update_tasks2.append((tid, harddepfail, tid in self.sqdata.valid))
Brad Bishop08902b02019-08-20 09:16:51 -04002647
Andrew Geissler517393d2023-01-13 08:55:19 -06002648 if update_tasks2:
Brad Bishop08902b02019-08-20 09:16:51 -04002649 self.sqdone = False
Patrick Williams92b42cb2022-09-03 06:53:57 -05002650 for mc in sorted(self.sqdata.multiconfigs):
Andrew Geissler517393d2023-01-13 08:55:19 -06002651 for tid in sorted([t[0] for t in update_tasks2]):
Patrick Williams92b42cb2022-09-03 06:53:57 -05002652 if mc_from_tid(tid) != mc:
2653 continue
2654 h = pending_hash_index(tid, self.rqdata)
2655 if h in self.sqdata.hashes and tid != self.sqdata.hashes[h]:
2656 self.sq_deferred[tid] = self.sqdata.hashes[h]
2657 bb.note("Deferring %s after %s" % (tid, self.sqdata.hashes[h]))
Andrew Geissler517393d2023-01-13 08:55:19 -06002658 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 -05002659
Andrew Geissler517393d2023-01-13 08:55:19 -06002660 for (tid, harddepfail, origvalid) in update_tasks2:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002661 if tid in self.sqdata.valid and not origvalid:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002662 hashequiv_logger.verbose("Setscene task %s became valid" % tid)
2663 if harddepfail:
Andrew Geissler517393d2023-01-13 08:55:19 -06002664 logger.debug2("%s has an unavailable hard dependency so skipping" % (tid))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002665 self.sq_task_failoutright(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002666
2667 if changed:
Andrew Geissler5199d832021-09-24 16:47:35 -05002668 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Patrick Williams169d7bc2024-01-05 11:33:25 -06002669 self.sq_needed_harddeps = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04002670 self.holdoff_need_update = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002671
Brad Bishop96ff1982019-08-19 13:50:42 -04002672 def scenequeue_updatecounters(self, task, fail=False):
Brad Bishop08902b02019-08-20 09:16:51 -04002673
Patrick Williams169d7bc2024-01-05 11:33:25 -06002674 if fail and task in self.sqdata.sq_harddeps:
2675 for dep in sorted(self.sqdata.sq_harddeps[task]):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002676 if dep in self.scenequeue_covered or dep in self.scenequeue_notcovered:
2677 # dependency could be already processed, e.g. noexec setscene task
2678 continue
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002679 noexec, stamppresent = check_setscene_stamps(dep, self.rqdata, self.rq, self.stampcache)
2680 if noexec or stamppresent:
2681 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002682 logger.debug2("%s was unavailable and is a hard dependency of %s so skipping" % (task, dep))
Brad Bishop96ff1982019-08-19 13:50:42 -04002683 self.sq_task_failoutright(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002684 continue
Patrick Williams169d7bc2024-01-05 11:33:25 -06002685 for dep in sorted(self.sqdata.sq_deps[task]):
Brad Bishop08902b02019-08-20 09:16:51 -04002686 if self.sqdata.sq_revdeps[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2687 if dep not in self.sq_buildable:
2688 self.sq_buildable.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002689
Brad Bishop96ff1982019-08-19 13:50:42 -04002690 next = set([task])
2691 while next:
2692 new = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002693 for t in sorted(next):
Brad Bishop96ff1982019-08-19 13:50:42 -04002694 self.tasks_scenequeue_done.add(t)
2695 # Look down the dependency chain for non-setscene things which this task depends on
2696 # and mark as 'done'
2697 for dep in self.rqdata.runtaskentries[t].depends:
2698 if dep in self.rqdata.runq_setscene_tids or dep in self.tasks_scenequeue_done:
2699 continue
2700 if self.rqdata.runtaskentries[dep].revdeps.issubset(self.tasks_scenequeue_done):
2701 new.add(dep)
Brad Bishop96ff1982019-08-19 13:50:42 -04002702 next = new
2703
Andrew Geissler5199d832021-09-24 16:47:35 -05002704 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002705 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002706
2707 def sq_task_completeoutright(self, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002708 """
2709 Mark a task as completed
2710 Look at the reverse dependencies and mark any task with
2711 completed dependencies as buildable
2712 """
2713
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002714 logger.debug('Found task %s which could be accelerated', task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002715 self.scenequeue_covered.add(task)
2716 self.scenequeue_updatecounters(task)
2717
Brad Bishop96ff1982019-08-19 13:50:42 -04002718 def sq_check_taskfail(self, task):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002719 if self.rqdata.setscene_ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002720 realtask = task.split('_setscene')[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002721 (mc, fn, taskname, taskfn) = split_tid_mcfn(realtask)
2722 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002723 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002724 logger.error('Task %s.%s failed' % (pn, taskname + "_setscene"))
2725 self.rq.state = runQueueCleanUp
2726
Brad Bishop96ff1982019-08-19 13:50:42 -04002727 def sq_task_complete(self, task):
Andrew Geissler5199d832021-09-24 16:47:35 -05002728 bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
Brad Bishop96ff1982019-08-19 13:50:42 -04002729 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002730
Brad Bishop96ff1982019-08-19 13:50:42 -04002731 def sq_task_fail(self, task, result):
Andrew Geissler5199d832021-09-24 16:47:35 -05002732 bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002733 self.scenequeue_notcovered.add(task)
2734 self.scenequeue_updatecounters(task, True)
Brad Bishop96ff1982019-08-19 13:50:42 -04002735 self.sq_check_taskfail(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002736
Brad Bishop96ff1982019-08-19 13:50:42 -04002737 def sq_task_failoutright(self, task):
2738 self.sq_running.add(task)
2739 self.sq_buildable.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002740 self.scenequeue_notcovered.add(task)
2741 self.scenequeue_updatecounters(task, True)
2742
Brad Bishop96ff1982019-08-19 13:50:42 -04002743 def sq_task_skip(self, task):
2744 self.sq_running.add(task)
2745 self.sq_buildable.add(task)
2746 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002747
Brad Bishop96ff1982019-08-19 13:50:42 -04002748 def sq_build_taskdepdata(self, task):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002749 def getsetscenedeps(tid):
2750 deps = set()
2751 (mc, fn, taskname, _) = split_tid_mcfn(tid)
2752 realtid = tid + "_setscene"
2753 idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
2754 for (depname, idependtask) in idepends:
2755 if depname not in self.rqdata.taskData[mc].build_targets:
2756 continue
2757
2758 depfn = self.rqdata.taskData[mc].build_targets[depname][0]
2759 if depfn is None:
2760 continue
2761 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2762 deps.add(deptid)
2763 return deps
2764
2765 taskdepdata = {}
2766 next = getsetscenedeps(task)
2767 next.add(task)
2768 while next:
2769 additional = []
2770 for revdep in next:
2771 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2772 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2773 deps = getsetscenedeps(revdep)
2774 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2775 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002776 unihash = self.rqdata.runtaskentries[revdep].unihash
Patrick Williamsb542dec2023-06-09 01:26:37 -05002777 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2778 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002779 for revdep2 in deps:
2780 if revdep2 not in taskdepdata:
2781 additional.append(revdep2)
2782 next = additional
2783
2784 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2785 return taskdepdata
2786
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002787 def check_setscene_ignore_tasks(self, tid):
2788 # Check task that is going to run against the ignore tasks list
Brad Bishop96ff1982019-08-19 13:50:42 -04002789 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2790 # Ignore covered tasks
2791 if tid in self.tasks_covered:
2792 return False
2793 # Ignore stamped tasks
2794 if self.rq.check_stamp_task(tid, taskname, cache=self.stampcache):
2795 return False
2796 # Ignore noexec tasks
2797 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2798 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2799 return False
2800
2801 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002802 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Brad Bishop96ff1982019-08-19 13:50:42 -04002803 if tid in self.rqdata.runq_setscene_tids:
Andrew Geissler595f6302022-01-24 19:11:47 +00002804 msg = ['Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)]
Brad Bishop96ff1982019-08-19 13:50:42 -04002805 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002806 msg = ['Task %s.%s attempted to execute unexpectedly' % (pn, taskname)]
Andrew Geissler82c905d2020-04-13 13:39:40 -05002807 for t in self.scenequeue_notcovered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002808 msg.append("\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash))
2809 msg.append('\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered))
2810 logger.error("".join(msg))
Brad Bishop96ff1982019-08-19 13:50:42 -04002811 return True
2812 return False
2813
2814class SQData(object):
2815 def __init__(self):
2816 # SceneQueue dependencies
2817 self.sq_deps = {}
2818 # SceneQueue reverse dependencies
2819 self.sq_revdeps = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002820 # Injected inter-setscene task dependencies
2821 self.sq_harddeps = {}
Patrick Williams169d7bc2024-01-05 11:33:25 -06002822 self.sq_harddeps_rev = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002823 # Cache of stamp files so duplicates can't run in parallel
2824 self.stamps = {}
2825 # Setscene tasks directly depended upon by the build
2826 self.unskippable = set()
2827 # List of setscene tasks which aren't present
2828 self.outrightfail = set()
2829 # A list of normal tasks a setscene task covers
2830 self.sq_covered_tasks = {}
2831
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002832def build_scenequeue_data(sqdata, rqdata, sqrq):
Brad Bishop96ff1982019-08-19 13:50:42 -04002833
2834 sq_revdeps = {}
2835 sq_revdeps_squash = {}
2836 sq_collated_deps = {}
2837
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002838 # We can't skip specified target tasks which aren't setscene tasks
2839 sqdata.cantskip = set(rqdata.target_tids)
2840 sqdata.cantskip.difference_update(rqdata.runq_setscene_tids)
2841 sqdata.cantskip.intersection_update(rqdata.runtaskentries)
2842
Brad Bishop96ff1982019-08-19 13:50:42 -04002843 # We need to construct a dependency graph for the setscene functions. Intermediate
2844 # dependencies between the setscene tasks only complicate the code. This code
2845 # therefore aims to collapse the huge runqueue dependency tree into a smaller one
2846 # only containing the setscene functions.
2847
2848 rqdata.init_progress_reporter.next_stage()
2849
2850 # First process the chains up to the first setscene task.
2851 endpoints = {}
2852 for tid in rqdata.runtaskentries:
2853 sq_revdeps[tid] = copy.copy(rqdata.runtaskentries[tid].revdeps)
2854 sq_revdeps_squash[tid] = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00002855 if not sq_revdeps[tid] and tid not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002856 #bb.warn("Added endpoint %s" % (tid))
2857 endpoints[tid] = set()
2858
2859 rqdata.init_progress_reporter.next_stage()
2860
2861 # Secondly process the chains between setscene tasks.
2862 for tid in rqdata.runq_setscene_tids:
2863 sq_collated_deps[tid] = set()
2864 #bb.warn("Added endpoint 2 %s" % (tid))
2865 for dep in rqdata.runtaskentries[tid].depends:
2866 if tid in sq_revdeps[dep]:
2867 sq_revdeps[dep].remove(tid)
2868 if dep not in endpoints:
2869 endpoints[dep] = set()
2870 #bb.warn(" Added endpoint 3 %s" % (dep))
2871 endpoints[dep].add(tid)
2872
2873 rqdata.init_progress_reporter.next_stage()
2874
2875 def process_endpoints(endpoints):
2876 newendpoints = {}
2877 for point, task in endpoints.items():
2878 tasks = set()
2879 if task:
2880 tasks |= task
2881 if sq_revdeps_squash[point]:
2882 tasks |= sq_revdeps_squash[point]
2883 if point not in rqdata.runq_setscene_tids:
2884 for t in tasks:
2885 sq_collated_deps[t].add(point)
2886 sq_revdeps_squash[point] = set()
2887 if point in rqdata.runq_setscene_tids:
2888 sq_revdeps_squash[point] = tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002889 continue
2890 for dep in rqdata.runtaskentries[point].depends:
2891 if point in sq_revdeps[dep]:
2892 sq_revdeps[dep].remove(point)
2893 if tasks:
2894 sq_revdeps_squash[dep] |= tasks
Andrew Geissler595f6302022-01-24 19:11:47 +00002895 if not sq_revdeps[dep] and dep not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002896 newendpoints[dep] = task
Andrew Geissler595f6302022-01-24 19:11:47 +00002897 if newendpoints:
Brad Bishop96ff1982019-08-19 13:50:42 -04002898 process_endpoints(newendpoints)
2899
2900 process_endpoints(endpoints)
2901
2902 rqdata.init_progress_reporter.next_stage()
2903
Brad Bishop08902b02019-08-20 09:16:51 -04002904 # Build a list of tasks which are "unskippable"
2905 # These are direct endpoints referenced by the build upto and including setscene tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002906 # Take the build endpoints (no revdeps) and find the sstate tasks they depend upon
2907 new = True
2908 for tid in rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00002909 if not rqdata.runtaskentries[tid].revdeps:
Brad Bishop96ff1982019-08-19 13:50:42 -04002910 sqdata.unskippable.add(tid)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002911 sqdata.unskippable |= sqdata.cantskip
Brad Bishop96ff1982019-08-19 13:50:42 -04002912 while new:
2913 new = False
Brad Bishop08902b02019-08-20 09:16:51 -04002914 orig = sqdata.unskippable.copy()
2915 for tid in sorted(orig, reverse=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04002916 if tid in rqdata.runq_setscene_tids:
2917 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00002918 if not rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04002919 # These are tasks which have no setscene tasks in their chain, need to mark as directly buildable
Brad Bishop96ff1982019-08-19 13:50:42 -04002920 sqrq.setbuildable(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002921 sqdata.unskippable |= rqdata.runtaskentries[tid].depends
Brad Bishop08902b02019-08-20 09:16:51 -04002922 if sqdata.unskippable != orig:
2923 new = True
2924
2925 sqrq.tasks_scenequeue_done |= sqdata.unskippable.difference(rqdata.runq_setscene_tids)
Brad Bishop96ff1982019-08-19 13:50:42 -04002926
2927 rqdata.init_progress_reporter.next_stage(len(rqdata.runtaskentries))
2928
2929 # Sanity check all dependencies could be changed to setscene task references
2930 for taskcounter, tid in enumerate(rqdata.runtaskentries):
2931 if tid in rqdata.runq_setscene_tids:
2932 pass
Andrew Geissler595f6302022-01-24 19:11:47 +00002933 elif sq_revdeps_squash[tid]:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002934 bb.msg.fatal("RunQueue", "Something went badly wrong during scenequeue generation, halting. Please report this problem.")
Brad Bishop96ff1982019-08-19 13:50:42 -04002935 else:
2936 del sq_revdeps_squash[tid]
2937 rqdata.init_progress_reporter.update(taskcounter)
2938
2939 rqdata.init_progress_reporter.next_stage()
2940
2941 # Resolve setscene inter-task dependencies
2942 # e.g. do_sometask_setscene[depends] = "targetname:do_someothertask_setscene"
2943 # Note that anything explicitly depended upon will have its reverse dependencies removed to avoid circular dependencies
2944 for tid in rqdata.runq_setscene_tids:
2945 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2946 realtid = tid + "_setscene"
2947 idepends = rqdata.taskData[mc].taskentries[realtid].idepends
Andrew Geissler517393d2023-01-13 08:55:19 -06002948 sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2949
Patrick Williams169d7bc2024-01-05 11:33:25 -06002950 sqdata.sq_harddeps_rev[tid] = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002951 for (depname, idependtask) in idepends:
2952
2953 if depname not in rqdata.taskData[mc].build_targets:
2954 continue
2955
2956 depfn = rqdata.taskData[mc].build_targets[depname][0]
2957 if depfn is None:
2958 continue
2959 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2960 if deptid not in rqdata.runtaskentries:
2961 bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (realtid, depfn, idependtask))
2962
Patrick Williams169d7bc2024-01-05 11:33:25 -06002963 logger.debug2("Adding hard setscene dependency %s for %s" % (deptid, tid))
2964
Brad Bishop96ff1982019-08-19 13:50:42 -04002965 if not deptid in sqdata.sq_harddeps:
2966 sqdata.sq_harddeps[deptid] = set()
2967 sqdata.sq_harddeps[deptid].add(tid)
Patrick Williams169d7bc2024-01-05 11:33:25 -06002968 sqdata.sq_harddeps_rev[tid].add(deptid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002969
2970 rqdata.init_progress_reporter.next_stage()
2971
Brad Bishop96ff1982019-08-19 13:50:42 -04002972 rqdata.init_progress_reporter.next_stage()
2973
2974 #for tid in sq_revdeps_squash:
2975 # data = ""
2976 # for dep in sq_revdeps_squash[tid]:
2977 # data = data + "\n %s" % dep
2978 # bb.warn("Task %s_setscene: is %s " % (tid, data))
2979
2980 sqdata.sq_revdeps = sq_revdeps_squash
Brad Bishop96ff1982019-08-19 13:50:42 -04002981 sqdata.sq_covered_tasks = sq_collated_deps
2982
2983 # Build reverse version of revdeps to populate deps structure
2984 for tid in sqdata.sq_revdeps:
2985 sqdata.sq_deps[tid] = set()
2986 for tid in sqdata.sq_revdeps:
2987 for dep in sqdata.sq_revdeps[tid]:
2988 sqdata.sq_deps[dep].add(tid)
2989
2990 rqdata.init_progress_reporter.next_stage()
2991
Brad Bishop00e122a2019-10-05 11:10:57 -04002992 sqdata.multiconfigs = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002993 for tid in sqdata.sq_revdeps:
Brad Bishop00e122a2019-10-05 11:10:57 -04002994 sqdata.multiconfigs.add(mc_from_tid(tid))
Andrew Geissler595f6302022-01-24 19:11:47 +00002995 if not sqdata.sq_revdeps[tid]:
Brad Bishop96ff1982019-08-19 13:50:42 -04002996 sqrq.sq_buildable.add(tid)
2997
Patrick Williams169d7bc2024-01-05 11:33:25 -06002998 rqdata.init_progress_reporter.next_stage()
Brad Bishop96ff1982019-08-19 13:50:42 -04002999
Brad Bishop00e122a2019-10-05 11:10:57 -04003000 sqdata.noexec = set()
3001 sqdata.stamppresent = set()
3002 sqdata.valid = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04003003
Patrick Williams213cb262021-08-07 19:21:33 -05003004 sqdata.hashes = {}
3005 sqrq.sq_deferred = {}
3006 for mc in sorted(sqdata.multiconfigs):
3007 for tid in sorted(sqdata.sq_revdeps):
3008 if mc_from_tid(tid) != mc:
3009 continue
3010 h = pending_hash_index(tid, rqdata)
3011 if h not in sqdata.hashes:
3012 sqdata.hashes[h] = tid
3013 else:
3014 sqrq.sq_deferred[tid] = sqdata.hashes[h]
Andrew Geissler8f840682023-07-21 09:09:43 -05003015 bb.debug(1, "Deferring %s after %s" % (tid, sqdata.hashes[h]))
Patrick Williams213cb262021-08-07 19:21:33 -05003016
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003017def check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=False):
3018
3019 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
3020
3021 taskdep = rqdata.dataCaches[mc].task_deps[taskfn]
3022
3023 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Andrew Geissler517393d2023-01-13 08:55:19 -06003024 bb.build.make_stamp_mcfn(taskname + "_setscene", taskfn)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003025 return True, False
3026
3027 if rq.check_stamp_task(tid, taskname + "_setscene", cache=stampcache):
3028 logger.debug2('Setscene stamp current for task %s', tid)
3029 return False, True
3030
3031 if rq.check_stamp_task(tid, taskname, recurse = True, cache=stampcache):
3032 logger.debug2('Normal stamp current for task %s', tid)
3033 return False, True
3034
3035 return False, False
3036
Brad Bishop1d80a2e2019-11-15 16:35:03 -05003037def update_scenequeue_data(tids, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True):
Brad Bishop00e122a2019-10-05 11:10:57 -04003038
3039 tocheck = set()
3040
3041 for tid in sorted(tids):
3042 if tid in sqdata.stamppresent:
3043 sqdata.stamppresent.remove(tid)
3044 if tid in sqdata.valid:
3045 sqdata.valid.remove(tid)
Andrew Geisslerc926e172021-05-07 16:11:35 -05003046 if tid in sqdata.outrightfail:
3047 sqdata.outrightfail.remove(tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04003048
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003049 noexec, stamppresent = check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04003050
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003051 if noexec:
Brad Bishop00e122a2019-10-05 11:10:57 -04003052 sqdata.noexec.add(tid)
3053 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003054 logger.debug2("%s is noexec so skipping setscene" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04003055 continue
3056
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003057 if stamppresent:
Brad Bishop00e122a2019-10-05 11:10:57 -04003058 sqdata.stamppresent.add(tid)
3059 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003060 logger.debug2("%s has a valid stamp, skipping" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04003061 continue
3062
3063 tocheck.add(tid)
3064
Brad Bishop1d80a2e2019-11-15 16:35:03 -05003065 sqdata.valid |= rq.validate_hashes(tocheck, cooker.data, len(sqdata.stamppresent), False, summary=summary)
Brad Bishop00e122a2019-10-05 11:10:57 -04003066
Patrick Williams213cb262021-08-07 19:21:33 -05003067 for tid in tids:
3068 if tid in sqdata.stamppresent:
3069 continue
3070 if tid in sqdata.valid:
3071 continue
3072 if tid in sqdata.noexec:
3073 continue
3074 if tid in sqrq.scenequeue_covered:
3075 continue
3076 if tid in sqrq.scenequeue_notcovered:
3077 continue
3078 if tid in sqrq.sq_deferred:
3079 continue
3080 sqdata.outrightfail.add(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003081 logger.debug2("%s already handled (fallthrough), skipping" % (tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04003082
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003083class TaskFailure(Exception):
3084 """
3085 Exception raised when a task in a runqueue fails
3086 """
3087 def __init__(self, x):
3088 self.args = x
3089
3090
3091class runQueueExitWait(bb.event.Event):
3092 """
3093 Event when waiting for task processes to exit
3094 """
3095
3096 def __init__(self, remain):
3097 self.remain = remain
3098 self.message = "Waiting for %s active tasks to finish" % remain
3099 bb.event.Event.__init__(self)
3100
3101class runQueueEvent(bb.event.Event):
3102 """
3103 Base runQueue event class
3104 """
3105 def __init__(self, task, stats, rq):
3106 self.taskid = task
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003107 self.taskstring = task
3108 self.taskname = taskname_from_tid(task)
3109 self.taskfile = fn_from_tid(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003110 self.taskhash = rq.rqdata.get_task_hash(task)
3111 self.stats = stats.copy()
3112 bb.event.Event.__init__(self)
3113
3114class sceneQueueEvent(runQueueEvent):
3115 """
3116 Base sceneQueue event class
3117 """
3118 def __init__(self, task, stats, rq, noexec=False):
3119 runQueueEvent.__init__(self, task, stats, rq)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003120 self.taskstring = task + "_setscene"
3121 self.taskname = taskname_from_tid(task) + "_setscene"
3122 self.taskfile = fn_from_tid(task)
3123 self.taskhash = rq.rqdata.get_task_hash(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003124
3125class runQueueTaskStarted(runQueueEvent):
3126 """
3127 Event notifying a task was started
3128 """
3129 def __init__(self, task, stats, rq, noexec=False):
3130 runQueueEvent.__init__(self, task, stats, rq)
3131 self.noexec = noexec
3132
3133class sceneQueueTaskStarted(sceneQueueEvent):
3134 """
3135 Event notifying a setscene task was started
3136 """
3137 def __init__(self, task, stats, rq, noexec=False):
3138 sceneQueueEvent.__init__(self, task, stats, rq)
3139 self.noexec = noexec
3140
3141class runQueueTaskFailed(runQueueEvent):
3142 """
3143 Event notifying a task failed
3144 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003145 def __init__(self, task, stats, exitcode, rq, fakeroot_log=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003146 runQueueEvent.__init__(self, task, stats, rq)
3147 self.exitcode = exitcode
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003148 self.fakeroot_log = fakeroot_log
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003149
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003150 def __str__(self):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003151 if self.fakeroot_log:
3152 return "Task (%s) failed with exit code '%s' \nPseudo log:\n%s" % (self.taskstring, self.exitcode, self.fakeroot_log)
3153 else:
3154 return "Task (%s) failed with exit code '%s'" % (self.taskstring, self.exitcode)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003155
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003156class sceneQueueTaskFailed(sceneQueueEvent):
3157 """
3158 Event notifying a setscene task failed
3159 """
3160 def __init__(self, task, stats, exitcode, rq):
3161 sceneQueueEvent.__init__(self, task, stats, rq)
3162 self.exitcode = exitcode
3163
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003164 def __str__(self):
3165 return "Setscene task (%s) failed with exit code '%s' - real task will be run instead" % (self.taskstring, self.exitcode)
3166
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003167class sceneQueueComplete(sceneQueueEvent):
3168 """
3169 Event when all the sceneQueue tasks are complete
3170 """
3171 def __init__(self, stats, rq):
3172 self.stats = stats.copy()
3173 bb.event.Event.__init__(self)
3174
3175class runQueueTaskCompleted(runQueueEvent):
3176 """
3177 Event notifying a task completed
3178 """
3179
3180class sceneQueueTaskCompleted(sceneQueueEvent):
3181 """
3182 Event notifying a setscene task completed
3183 """
3184
3185class runQueueTaskSkipped(runQueueEvent):
3186 """
3187 Event notifying a task was skipped
3188 """
3189 def __init__(self, task, stats, rq, reason):
3190 runQueueEvent.__init__(self, task, stats, rq)
3191 self.reason = reason
3192
Brad Bishop08902b02019-08-20 09:16:51 -04003193class taskUniHashUpdate(bb.event.Event):
3194 """
3195 Base runQueue event class
3196 """
3197 def __init__(self, task, unihash):
3198 self.taskid = task
3199 self.unihash = unihash
3200 bb.event.Event.__init__(self)
3201
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003202class runQueuePipe():
3203 """
3204 Abstraction for a pipe between a worker thread and the server
3205 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003206 def __init__(self, pipein, pipeout, d, rq, rqexec, fakerootlogs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003207 self.input = pipein
3208 if pipeout:
3209 pipeout.close()
3210 bb.utils.nonblockingfd(self.input)
Andrew Geissler220dafd2023-10-04 10:18:08 -05003211 self.queue = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003212 self.d = d
3213 self.rq = rq
3214 self.rqexec = rqexec
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003215 self.fakerootlogs = fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003216
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003217 def read(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003218 for workers, name in [(self.rq.worker, "Worker"), (self.rq.fakeworker, "Fakeroot")]:
3219 for worker in workers.values():
3220 worker.process.poll()
3221 if worker.process.returncode is not None and not self.rq.teardown:
3222 bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, worker.process.pid, str(worker.process.returncode)))
3223 self.rq.finish_runqueue(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003224
3225 start = len(self.queue)
3226 try:
Andrew Geissler220dafd2023-10-04 10:18:08 -05003227 self.queue.extend(self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003228 except (OSError, IOError) as e:
3229 if e.errno != errno.EAGAIN:
3230 raise
3231 end = len(self.queue)
3232 found = True
Andrew Geissler595f6302022-01-24 19:11:47 +00003233 while found and self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003234 found = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003235 index = self.queue.find(b"</event>")
3236 while index != -1 and self.queue.startswith(b"<event>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003237 try:
3238 event = pickle.loads(self.queue[7:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003239 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
3240 if isinstance(e, pickle.UnpicklingError) and "truncated" in str(e):
3241 # The pickled data could contain "</event>" so search for the next occurance
3242 # unpickling again, this should be the only way an unpickle error could occur
3243 index = self.queue.find(b"</event>", index + 1)
3244 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003245 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
3246 bb.event.fire_from_worker(event, self.d)
Brad Bishop08902b02019-08-20 09:16:51 -04003247 if isinstance(event, taskUniHashUpdate):
3248 self.rqexec.updated_taskhash_queue.append((event.taskid, event.unihash))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003249 found = True
3250 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003251 index = self.queue.find(b"</event>")
3252 index = self.queue.find(b"</exitcode>")
3253 while index != -1 and self.queue.startswith(b"<exitcode>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003254 try:
3255 task, status = pickle.loads(self.queue[10:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003256 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003257 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003258 (_, _, _, taskfn) = split_tid_mcfn(task)
3259 fakerootlog = None
3260 if self.fakerootlogs and taskfn and taskfn in self.fakerootlogs:
3261 fakerootlog = self.fakerootlogs[taskfn]
3262 self.rqexec.runqueue_process_waitpid(task, status, fakerootlog=fakerootlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003263 found = True
3264 self.queue = self.queue[index+11:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003265 index = self.queue.find(b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003266 return (end > start)
3267
3268 def close(self):
3269 while self.read():
3270 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00003271 if self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003272 print("Warning, worker left partial message: %s" % self.queue)
3273 self.input.close()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003274
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003275def get_setscene_enforce_ignore_tasks(d, targets):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003276 if d.getVar('BB_SETSCENE_ENFORCE') != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003277 return None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003278 ignore_tasks = (d.getVar("BB_SETSCENE_ENFORCE_IGNORE_TASKS") or "").split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003279 outlist = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003280 for item in ignore_tasks[:]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003281 if item.startswith('%:'):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003282 for (mc, target, task, fn) in targets:
3283 outlist.append(target + ':' + item.split(':')[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003284 else:
3285 outlist.append(item)
3286 return outlist
3287
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003288def check_setscene_enforce_ignore_tasks(pn, taskname, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003289 import fnmatch
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003290 if ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003291 item = '%s:%s' % (pn, taskname)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003292 for ignore_tasks in ignore_tasks:
3293 if fnmatch.fnmatch(item, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003294 return True
3295 return False
3296 return True