blob: 56147c50a8a2d750bc6a8adc36be376c693f6216 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""
2BitBake 'RunQueue' implementation
3
4Handles preparation and execution of a queue of tasks
5"""
6
7# Copyright (C) 2006-2007 Richard Purdie
8#
Brad Bishopc342db32019-05-15 21:57:59 -04009# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011
12import copy
13import os
14import sys
Patrick Williamsc124f4f2015-09-15 14:41:29 -050015import stat
Patrick Williamsc124f4f2015-09-15 14:41:29 -050016import errno
17import logging
18import re
19import bb
Andrew Geissler82c905d2020-04-13 13:39:40 -050020from bb import msg, event
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021from bb import monitordisk
22import subprocess
Patrick Williamsc0f7c042017-02-23 20:41:17 -060023import pickle
Brad Bishop6e60e8b2018-02-01 10:27:11 -050024from multiprocessing import Process
Brad Bishop19323692019-04-05 15:28:33 -040025import shlex
Brad Bishop96ff1982019-08-19 13:50:42 -040026import pprint
Patrick Williamsdb4c27e2022-08-05 08:10:29 -050027import time
Patrick Williamsc124f4f2015-09-15 14:41:29 -050028
29bblogger = logging.getLogger("BitBake")
30logger = logging.getLogger("BitBake.RunQueue")
Andrew Geissler82c905d2020-04-13 13:39:40 -050031hashequiv_logger = logging.getLogger("BitBake.RunQueue.HashEquiv")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032
Brad Bishop19323692019-04-05 15:28:33 -040033__find_sha256__ = re.compile( r'(?i)(?<![a-z0-9])[a-f0-9]{64}(?![a-z0-9])' )
Patrick Williamsc124f4f2015-09-15 14:41:29 -050034
Patrick Williamsc0f7c042017-02-23 20:41:17 -060035def fn_from_tid(tid):
36 return tid.rsplit(":", 1)[0]
37
38def taskname_from_tid(tid):
39 return tid.rsplit(":", 1)[1]
40
Andrew Geissler99467da2019-02-25 18:54:23 -060041def mc_from_tid(tid):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060042 if tid.startswith('mc:') and tid.count(':') >= 2:
Andrew Geissler99467da2019-02-25 18:54:23 -060043 return tid.split(':')[1]
44 return ""
45
Patrick Williamsc0f7c042017-02-23 20:41:17 -060046def split_tid(tid):
47 (mc, fn, taskname, _) = split_tid_mcfn(tid)
48 return (mc, fn, taskname)
49
Andrew Geissler5a43b432020-06-13 10:46:56 -050050def split_mc(n):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060051 if n.startswith("mc:") and n.count(':') >= 2:
Andrew Geissler5a43b432020-06-13 10:46:56 -050052 _, mc, n = n.split(":", 2)
53 return (mc, n)
54 return ('', n)
55
Patrick Williamsc0f7c042017-02-23 20:41:17 -060056def split_tid_mcfn(tid):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060057 if tid.startswith('mc:') and tid.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060058 elems = tid.split(':')
59 mc = elems[1]
60 fn = ":".join(elems[2:-1])
61 taskname = elems[-1]
Brad Bishop15ae2502019-06-18 21:44:24 -040062 mcfn = "mc:" + mc + ":" + fn
Patrick Williamsc0f7c042017-02-23 20:41:17 -060063 else:
64 tid = tid.rsplit(":", 1)
65 mc = ""
66 fn = tid[0]
67 taskname = tid[1]
68 mcfn = fn
69
70 return (mc, fn, taskname, mcfn)
71
72def build_tid(mc, fn, taskname):
73 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -040074 return "mc:" + mc + ":" + fn + ":" + taskname
Patrick Williamsc0f7c042017-02-23 20:41:17 -060075 return fn + ":" + taskname
76
Brad Bishop96ff1982019-08-19 13:50:42 -040077# Index used to pair up potentially matching multiconfig tasks
78# We match on PN, taskname and hash being equal
79def pending_hash_index(tid, rqdata):
80 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
81 pn = rqdata.dataCaches[mc].pkg_fn[taskfn]
Brad Bishop00e122a2019-10-05 11:10:57 -040082 h = rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -040083 return pn + ":" + "taskname" + h
84
Patrick Williamsc124f4f2015-09-15 14:41:29 -050085class RunQueueStats:
86 """
87 Holds statistics on the tasks handled by the associated runQueue
88 """
Andrew Geissler5199d832021-09-24 16:47:35 -050089 def __init__(self, total, setscene_total):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090 self.completed = 0
91 self.skipped = 0
92 self.failed = 0
93 self.active = 0
Andrew Geissler5199d832021-09-24 16:47:35 -050094 self.setscene_active = 0
95 self.setscene_covered = 0
96 self.setscene_notcovered = 0
97 self.setscene_total = setscene_total
Patrick Williamsc124f4f2015-09-15 14:41:29 -050098 self.total = total
99
100 def copy(self):
Andrew Geissler5199d832021-09-24 16:47:35 -0500101 obj = self.__class__(self.total, self.setscene_total)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102 obj.__dict__.update(self.__dict__)
103 return obj
104
105 def taskFailed(self):
106 self.active = self.active - 1
107 self.failed = self.failed + 1
108
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800109 def taskCompleted(self):
110 self.active = self.active - 1
111 self.completed = self.completed + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500112
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800113 def taskSkipped(self):
114 self.active = self.active + 1
115 self.skipped = self.skipped + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500116
117 def taskActive(self):
118 self.active = self.active + 1
119
Andrew Geissler5199d832021-09-24 16:47:35 -0500120 def updateCovered(self, covered, notcovered):
121 self.setscene_covered = covered
122 self.setscene_notcovered = notcovered
123
124 def updateActiveSetscene(self, active):
125 self.setscene_active = active
126
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127# These values indicate the next step due to be run in the
128# runQueue state machine
129runQueuePrepare = 2
130runQueueSceneInit = 3
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131runQueueRunning = 6
132runQueueFailed = 7
133runQueueCleanUp = 8
134runQueueComplete = 9
135
136class RunQueueScheduler(object):
137 """
138 Control the order tasks are scheduled in.
139 """
140 name = "basic"
141
142 def __init__(self, runqueue, rqdata):
143 """
144 The default scheduler just returns the first buildable task (the
145 priority map is sorted by task number)
146 """
147 self.rq = runqueue
148 self.rqdata = rqdata
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600149 self.numTasks = len(self.rqdata.runtaskentries)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600151 self.prio_map = [self.rqdata.runtaskentries.keys()]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500152
Brad Bishop08902b02019-08-20 09:16:51 -0400153 self.buildable = set()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800154 self.skip_maxthread = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500155 self.stamps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600156 for tid in self.rqdata.runtaskentries:
157 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -0600158 self.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600159 if tid in self.rq.runq_buildable:
160 self.buildable.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500161
162 self.rev_prio_map = None
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500163 self.is_pressure_usable()
164
165 def is_pressure_usable(self):
166 """
167 If monitoring pressure, return True if pressure files can be open and read. For example
168 openSUSE /proc/pressure/* files have readable file permissions but when read the error EOPNOTSUPP (Operation not supported)
169 is returned.
170 """
Patrick Williams92b42cb2022-09-03 06:53:57 -0500171 if self.rq.max_cpu_pressure or self.rq.max_io_pressure or self.rq.max_memory_pressure:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500172 try:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500173 with open("/proc/pressure/cpu") as cpu_pressure_fds, \
174 open("/proc/pressure/io") as io_pressure_fds, \
175 open("/proc/pressure/memory") as memory_pressure_fds:
176
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500177 self.prev_cpu_pressure = cpu_pressure_fds.readline().split()[4].split("=")[1]
178 self.prev_io_pressure = io_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williams92b42cb2022-09-03 06:53:57 -0500179 self.prev_memory_pressure = memory_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500180 self.prev_pressure_time = time.time()
181 self.check_pressure = True
182 except:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500183 bb.note("The /proc/pressure files can't be read. Continuing build without monitoring pressure")
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500184 self.check_pressure = False
185 else:
186 self.check_pressure = False
187
188 def exceeds_max_pressure(self):
189 """
190 Monitor the difference in total pressure at least once per second, if
Patrick Williams92b42cb2022-09-03 06:53:57 -0500191 BB_PRESSURE_MAX_{CPU|IO|MEMORY} are set, return True if above threshold.
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500192 """
193 if self.check_pressure:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500194 with open("/proc/pressure/cpu") as cpu_pressure_fds, \
195 open("/proc/pressure/io") as io_pressure_fds, \
196 open("/proc/pressure/memory") as memory_pressure_fds:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500197 # extract "total" from /proc/pressure/{cpu|io}
198 curr_cpu_pressure = cpu_pressure_fds.readline().split()[4].split("=")[1]
199 curr_io_pressure = io_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williams92b42cb2022-09-03 06:53:57 -0500200 curr_memory_pressure = memory_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500201 now = time.time()
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500202 tdiff = now - self.prev_pressure_time
Andrew Geissler5082cc72023-09-11 08:41:39 -0400203 psi_accumulation_interval = 1.0
204 cpu_pressure = (float(curr_cpu_pressure) - float(self.prev_cpu_pressure)) / tdiff
205 io_pressure = (float(curr_io_pressure) - float(self.prev_io_pressure)) / tdiff
206 memory_pressure = (float(curr_memory_pressure) - float(self.prev_memory_pressure)) / tdiff
207 exceeds_cpu_pressure = self.rq.max_cpu_pressure and cpu_pressure > self.rq.max_cpu_pressure
208 exceeds_io_pressure = self.rq.max_io_pressure and io_pressure > self.rq.max_io_pressure
209 exceeds_memory_pressure = self.rq.max_memory_pressure and memory_pressure > self.rq.max_memory_pressure
210
211 if tdiff > psi_accumulation_interval:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500212 self.prev_cpu_pressure = curr_cpu_pressure
213 self.prev_io_pressure = curr_io_pressure
Patrick Williams92b42cb2022-09-03 06:53:57 -0500214 self.prev_memory_pressure = curr_memory_pressure
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500215 self.prev_pressure_time = now
Andrew Geissler5082cc72023-09-11 08:41:39 -0400216
Andrew Geissler8f840682023-07-21 09:09:43 -0500217 pressure_state = (exceeds_cpu_pressure, exceeds_io_pressure, exceeds_memory_pressure)
Andrew Geissler5082cc72023-09-11 08:41:39 -0400218 pressure_values = (round(cpu_pressure,1), self.rq.max_cpu_pressure, round(io_pressure,1), self.rq.max_io_pressure, round(memory_pressure,1), self.rq.max_memory_pressure)
Andrew Geissler8f840682023-07-21 09:09:43 -0500219 if hasattr(self, "pressure_state") and pressure_state != self.pressure_state:
Andrew Geissler5082cc72023-09-11 08:41:39 -0400220 bb.note("Pressure status changed to CPU: %s, IO: %s, Mem: %s (CPU: %s/%s, IO: %s/%s, Mem: %s/%s) - using %s/%s bitbake threads" % (pressure_state + pressure_values + (len(self.rq.runq_running.difference(self.rq.runq_complete)), self.rq.number_tasks)))
Andrew Geissler8f840682023-07-21 09:09:43 -0500221 self.pressure_state = pressure_state
Patrick Williams92b42cb2022-09-03 06:53:57 -0500222 return (exceeds_cpu_pressure or exceeds_io_pressure or exceeds_memory_pressure)
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500223 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500224
225 def next_buildable_task(self):
226 """
227 Return the id of the first task we find that is buildable
228 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500229 # Once tasks are running we don't need to worry about them again
230 self.buildable.difference_update(self.rq.runq_running)
Brad Bishop08902b02019-08-20 09:16:51 -0400231 buildable = set(self.buildable)
Brad Bishop08902b02019-08-20 09:16:51 -0400232 buildable.difference_update(self.rq.holdoff_tasks)
233 buildable.intersection_update(self.rq.tasks_covered | self.rq.tasks_notcovered)
Brad Bishop96ff1982019-08-19 13:50:42 -0400234 if not buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500235 return None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800236
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500237 # Bitbake requires that at least one task be active. Only check for pressure if
238 # this is the case, otherwise the pressure limitation could result in no tasks
239 # being active and no new tasks started thereby, at times, breaking the scheduler.
240 if self.rq.stats.active and self.exceeds_max_pressure():
241 return None
242
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800243 # Filter out tasks that have a max number of threads that have been exceeded
244 skip_buildable = {}
245 for running in self.rq.runq_running.difference(self.rq.runq_complete):
246 rtaskname = taskname_from_tid(running)
247 if rtaskname not in self.skip_maxthread:
248 self.skip_maxthread[rtaskname] = self.rq.cfgData.getVarFlag(rtaskname, "number_threads")
249 if not self.skip_maxthread[rtaskname]:
250 continue
251 if rtaskname in skip_buildable:
252 skip_buildable[rtaskname] += 1
253 else:
254 skip_buildable[rtaskname] = 1
255
Brad Bishop96ff1982019-08-19 13:50:42 -0400256 if len(buildable) == 1:
Brad Bishop08902b02019-08-20 09:16:51 -0400257 tid = buildable.pop()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800258 taskname = taskname_from_tid(tid)
259 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
260 return None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600261 stamp = self.stamps[tid]
262 if stamp not in self.rq.build_stamps.values():
263 return tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500264
265 if not self.rev_prio_map:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600266 self.rev_prio_map = {}
267 for tid in self.rqdata.runtaskentries:
268 self.rev_prio_map[tid] = self.prio_map.index(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500269
270 best = None
271 bestprio = None
Brad Bishop96ff1982019-08-19 13:50:42 -0400272 for tid in buildable:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800273 taskname = taskname_from_tid(tid)
274 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
275 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600276 prio = self.rev_prio_map[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500277 if bestprio is None or bestprio > prio:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600278 stamp = self.stamps[tid]
279 if stamp in self.rq.build_stamps.values():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500280 continue
281 bestprio = prio
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600282 best = tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283
284 return best
285
286 def next(self):
287 """
288 Return the id of the task we should build next
289 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800290 if self.rq.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500291 return self.next_buildable_task()
292
Brad Bishop316dfdd2018-06-25 12:45:53 -0400293 def newbuildable(self, task):
Brad Bishop08902b02019-08-20 09:16:51 -0400294 self.buildable.add(task)
295
296 def removebuildable(self, task):
297 self.buildable.remove(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500298
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500299 def describe_task(self, taskid):
300 result = 'ID %s' % taskid
301 if self.rev_prio_map:
302 result = result + (' pri %d' % self.rev_prio_map[taskid])
303 return result
304
305 def dump_prio(self, comment):
306 bb.debug(3, '%s (most important first):\n%s' %
307 (comment,
308 '\n'.join(['%d. %s' % (index + 1, self.describe_task(taskid)) for
309 index, taskid in enumerate(self.prio_map)])))
310
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311class RunQueueSchedulerSpeed(RunQueueScheduler):
312 """
313 A scheduler optimised for speed. The priority map is sorted by task weight,
314 heavier weighted tasks (tasks needed by the most other tasks) are run first.
315 """
316 name = "speed"
317
318 def __init__(self, runqueue, rqdata):
319 """
320 The priority map is sorted by task weight.
321 """
322 RunQueueScheduler.__init__(self, runqueue, rqdata)
323
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600324 weights = {}
325 for tid in self.rqdata.runtaskentries:
326 weight = self.rqdata.runtaskentries[tid].weight
327 if not weight in weights:
328 weights[weight] = []
329 weights[weight].append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500330
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600331 self.prio_map = []
332 for weight in sorted(weights):
333 for w in weights[weight]:
334 self.prio_map.append(w)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500335
336 self.prio_map.reverse()
337
338class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed):
339 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500340 A scheduler optimised to complete .bb files as quickly as possible. The
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500341 priority map is sorted by task weight, but then reordered so once a given
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500342 .bb file starts to build, it's completed as quickly as possible by
343 running all tasks related to the same .bb file one after the after.
344 This works well where disk space is at a premium and classes like OE's
345 rm_work are in force.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500346 """
347 name = "completion"
348
349 def __init__(self, runqueue, rqdata):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500350 super(RunQueueSchedulerCompletion, self).__init__(runqueue, rqdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500352 # Extract list of tasks for each recipe, with tasks sorted
353 # ascending from "must run first" (typically do_fetch) to
354 # "runs last" (do_build). The speed scheduler prioritizes
355 # tasks that must run first before the ones that run later;
356 # this is what we depend on here.
357 task_lists = {}
358 for taskid in self.prio_map:
359 fn, taskname = taskid.rsplit(':', 1)
360 task_lists.setdefault(fn, []).append(taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500361
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500362 # Now unify the different task lists. The strategy is that
363 # common tasks get skipped and new ones get inserted after the
364 # preceeding common one(s) as they are found. Because task
365 # lists should differ only by their number of tasks, but not
366 # the ordering of the common tasks, this should result in a
367 # deterministic result that is a superset of the individual
368 # task ordering.
369 all_tasks = []
370 for recipe, new_tasks in task_lists.items():
371 index = 0
372 old_task = all_tasks[index] if index < len(all_tasks) else None
373 for new_task in new_tasks:
374 if old_task == new_task:
375 # Common task, skip it. This is the fast-path which
376 # avoids a full search.
377 index += 1
378 old_task = all_tasks[index] if index < len(all_tasks) else None
379 else:
380 try:
381 index = all_tasks.index(new_task)
382 # Already present, just not at the current
383 # place. We re-synchronized by changing the
384 # index so that it matches again. Now
385 # move on to the next existing task.
386 index += 1
387 old_task = all_tasks[index] if index < len(all_tasks) else None
388 except ValueError:
389 # Not present. Insert before old_task, which
390 # remains the same (but gets shifted back).
391 all_tasks.insert(index, new_task)
392 index += 1
393 bb.debug(3, 'merged task list: %s' % all_tasks)
394
395 # Now reverse the order so that tasks that finish the work on one
396 # recipe are considered more imporant (= come first). The ordering
397 # is now so that do_build is most important.
398 all_tasks.reverse()
399
400 # Group tasks of the same kind before tasks of less important
401 # kinds at the head of the queue (because earlier = lower
402 # priority number = runs earlier), while preserving the
403 # ordering by recipe. If recipe foo is more important than
404 # bar, then the goal is to work on foo's do_populate_sysroot
405 # before bar's do_populate_sysroot and on the more important
406 # tasks of foo before any of the less important tasks in any
407 # other recipe (if those other recipes are more important than
408 # foo).
409 #
410 # All of this only applies when tasks are runable. Explicit
411 # dependencies still override this ordering by priority.
412 #
413 # Here's an example why this priority re-ordering helps with
414 # minimizing disk usage. Consider a recipe foo with a higher
415 # priority than bar where foo DEPENDS on bar. Then the
416 # implicit rule (from base.bbclass) is that foo's do_configure
417 # depends on bar's do_populate_sysroot. This ensures that
418 # bar's do_populate_sysroot gets done first. Normally the
419 # tasks from foo would continue to run once that is done, and
420 # bar only gets completed and cleaned up later. By ordering
421 # bar's task that depend on bar's do_populate_sysroot before foo's
422 # do_configure, that problem gets avoided.
423 task_index = 0
424 self.dump_prio('original priorities')
425 for task in all_tasks:
426 for index in range(task_index, self.numTasks):
427 taskid = self.prio_map[index]
428 taskname = taskid.rsplit(':', 1)[1]
429 if taskname == task:
430 del self.prio_map[index]
431 self.prio_map.insert(task_index, taskid)
432 task_index += 1
433 self.dump_prio('completion priorities')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500434
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600435class RunTaskEntry(object):
436 def __init__(self):
437 self.depends = set()
438 self.revdeps = set()
439 self.hash = None
Brad Bishop19323692019-04-05 15:28:33 -0400440 self.unihash = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600441 self.task = None
442 self.weight = 1
443
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444class RunQueueData:
445 """
446 BitBake Run Queue implementation
447 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600448 def __init__(self, rq, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500449 self.cooker = cooker
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600450 self.dataCaches = dataCaches
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500451 self.taskData = taskData
452 self.targets = targets
453 self.rq = rq
454 self.warn_multi_bb = False
455
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000456 self.multi_provider_allowed = (cfgData.getVar("BB_MULTI_PROVIDER_ALLOWED") or "").split()
457 self.setscene_ignore_tasks = get_setscene_enforce_ignore_tasks(cfgData, targets)
458 self.setscene_ignore_tasks_checked = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500459 self.setscene_enforce = (cfgData.getVar('BB_SETSCENE_ENFORCE') == "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600460 self.init_progress_reporter = bb.progress.DummyMultiStageProcessProgressReporter()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500461
462 self.reset()
463
464 def reset(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600465 self.runtaskentries = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500466
467 def runq_depends_names(self, ids):
468 import re
469 ret = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600470 for id in ids:
471 nam = os.path.basename(id)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500472 nam = re.sub("_[^,]*,", ",", nam)
473 ret.extend([nam])
474 return ret
475
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600476 def get_task_hash(self, tid):
477 return self.runtaskentries[tid].hash
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500478
Brad Bishop19323692019-04-05 15:28:33 -0400479 def get_task_unihash(self, tid):
480 return self.runtaskentries[tid].unihash
481
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600482 def get_user_idstring(self, tid, task_name_suffix = ""):
483 return tid + task_name_suffix
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500484
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500485 def get_short_user_idstring(self, task, task_name_suffix = ""):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500486 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
487 pn = self.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600488 taskname = taskname_from_tid(task) + task_name_suffix
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500489 return "%s:%s" % (pn, taskname)
490
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491 def circular_depchains_handler(self, tasks):
492 """
493 Some tasks aren't buildable, likely due to circular dependency issues.
494 Identify the circular dependencies and print them in a user readable format.
495 """
496 from copy import deepcopy
497
498 valid_chains = []
499 explored_deps = {}
500 msgs = []
501
Andrew Geissler99467da2019-02-25 18:54:23 -0600502 class TooManyLoops(Exception):
503 pass
504
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500505 def chain_reorder(chain):
506 """
507 Reorder a dependency chain so the lowest task id is first
508 """
509 lowest = 0
510 new_chain = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600511 for entry in range(len(chain)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500512 if chain[entry] < chain[lowest]:
513 lowest = entry
514 new_chain.extend(chain[lowest:])
515 new_chain.extend(chain[:lowest])
516 return new_chain
517
518 def chain_compare_equal(chain1, chain2):
519 """
520 Compare two dependency chains and see if they're the same
521 """
522 if len(chain1) != len(chain2):
523 return False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600524 for index in range(len(chain1)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500525 if chain1[index] != chain2[index]:
526 return False
527 return True
528
529 def chain_array_contains(chain, chain_array):
530 """
531 Return True if chain_array contains chain
532 """
533 for ch in chain_array:
534 if chain_compare_equal(ch, chain):
535 return True
536 return False
537
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600538 def find_chains(tid, prev_chain):
539 prev_chain.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500540 total_deps = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600541 total_deps.extend(self.runtaskentries[tid].revdeps)
542 for revdep in self.runtaskentries[tid].revdeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500543 if revdep in prev_chain:
544 idx = prev_chain.index(revdep)
545 # To prevent duplicates, reorder the chain to start with the lowest taskid
546 # and search through an array of those we've already printed
547 chain = prev_chain[idx:]
548 new_chain = chain_reorder(chain)
549 if not chain_array_contains(new_chain, valid_chains):
550 valid_chains.append(new_chain)
551 msgs.append("Dependency loop #%d found:\n" % len(valid_chains))
552 for dep in new_chain:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600553 msgs.append(" Task %s (dependent Tasks %s)\n" % (dep, self.runq_depends_names(self.runtaskentries[dep].depends)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500554 msgs.append("\n")
555 if len(valid_chains) > 10:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000556 msgs.append("Halted dependency loops search after 10 matches.\n")
Andrew Geissler99467da2019-02-25 18:54:23 -0600557 raise TooManyLoops
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500558 continue
559 scan = False
560 if revdep not in explored_deps:
561 scan = True
562 elif revdep in explored_deps[revdep]:
563 scan = True
564 else:
565 for dep in prev_chain:
566 if dep in explored_deps[revdep]:
567 scan = True
568 if scan:
569 find_chains(revdep, copy.deepcopy(prev_chain))
570 for dep in explored_deps[revdep]:
571 if dep not in total_deps:
572 total_deps.append(dep)
573
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600574 explored_deps[tid] = total_deps
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500575
Andrew Geissler99467da2019-02-25 18:54:23 -0600576 try:
577 for task in tasks:
578 find_chains(task, [])
579 except TooManyLoops:
580 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500581
582 return msgs
583
584 def calculate_task_weights(self, endpoints):
585 """
586 Calculate a number representing the "weight" of each task. Heavier weighted tasks
587 have more dependencies and hence should be executed sooner for maximum speed.
588
589 This function also sanity checks the task list finding tasks that are not
590 possible to execute due to circular dependencies.
591 """
592
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600593 numTasks = len(self.runtaskentries)
594 weight = {}
595 deps_left = {}
596 task_done = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600598 for tid in self.runtaskentries:
599 task_done[tid] = False
600 weight[tid] = 1
601 deps_left[tid] = len(self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600603 for tid in endpoints:
604 weight[tid] = 10
605 task_done[tid] = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500606
607 while True:
608 next_points = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600609 for tid in endpoints:
610 for revdep in self.runtaskentries[tid].depends:
611 weight[revdep] = weight[revdep] + weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500612 deps_left[revdep] = deps_left[revdep] - 1
613 if deps_left[revdep] == 0:
614 next_points.append(revdep)
615 task_done[revdep] = True
616 endpoints = next_points
Andrew Geissler595f6302022-01-24 19:11:47 +0000617 if not next_points:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500618 break
619
620 # Circular dependency sanity check
621 problem_tasks = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600622 for tid in self.runtaskentries:
623 if task_done[tid] is False or deps_left[tid] != 0:
624 problem_tasks.append(tid)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600625 logger.debug2("Task %s is not buildable", tid)
626 logger.debug2("(Complete marker was %s and the remaining dependency count was %s)\n", task_done[tid], deps_left[tid])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600627 self.runtaskentries[tid].weight = weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500628
629 if problem_tasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600630 message = "%s unbuildable tasks were found.\n" % len(problem_tasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631 message = message + "These are usually caused by circular dependencies and any circular dependency chains found will be printed below. Increase the debug level to see a list of unbuildable tasks.\n\n"
632 message = message + "Identifying dependency loops (this may take a short while)...\n"
633 logger.error(message)
634
635 msgs = self.circular_depchains_handler(problem_tasks)
636
637 message = "\n"
638 for msg in msgs:
639 message = message + msg
640 bb.msg.fatal("RunQueue", message)
641
642 return weight
643
644 def prepare(self):
645 """
646 Turn a set of taskData into a RunQueue and compute data needed
647 to optimise the execution order.
648 """
649
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600650 runq_build = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 recursivetasks = {}
652 recursiveitasks = {}
653 recursivetasksselfref = set()
654
655 taskData = self.taskData
656
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600657 found = False
658 for mc in self.taskData:
Andrew Geissler595f6302022-01-24 19:11:47 +0000659 if taskData[mc].taskentries:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600660 found = True
661 break
662 if not found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663 # Nothing to do
664 return 0
665
Andrew Geissler517393d2023-01-13 08:55:19 -0600666 bb.parse.siggen.setup_datacache(self.dataCaches)
667
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600668 self.init_progress_reporter.start()
669 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600670 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500671
672 # Step A - Work out a list of tasks to run
673 #
674 # Taskdata gives us a list of possible providers for every build and run
675 # target ordered by priority. It also gives information on each of those
676 # providers.
677 #
678 # To create the actual list of tasks to execute we fix the list of
679 # providers and then resolve the dependencies into task IDs. This
680 # process is repeated for each type of dependency (tdepends, deptask,
681 # rdeptast, recrdeptask, idepends).
682
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600683 def add_build_dependencies(depids, tasknames, depends, mc):
684 for depname in depids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500685 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600686 if depname not in taskData[mc].build_targets or not taskData[mc].build_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500687 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600688 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689 if depdata is None:
690 continue
691 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600692 t = depdata + ":" + taskname
693 if t in taskData[mc].taskentries:
694 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500695
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600696 def add_runtime_dependencies(depids, tasknames, depends, mc):
697 for depname in depids:
698 if depname not in taskData[mc].run_targets or not taskData[mc].run_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500699 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600700 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500701 if depdata is None:
702 continue
703 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600704 t = depdata + ":" + taskname
705 if t in taskData[mc].taskentries:
706 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800708 def add_mc_dependencies(mc, tid):
709 mcdeps = taskData[mc].get_mcdepends()
710 for dep in mcdeps:
711 mcdependency = dep.split(':')
712 pn = mcdependency[3]
713 frommc = mcdependency[1]
714 mcdep = mcdependency[2]
715 deptask = mcdependency[4]
Andrew Geissler517393d2023-01-13 08:55:19 -0600716 if mcdep not in taskData:
717 bb.fatal("Multiconfig '%s' is referenced in multiconfig dependency '%s' but not enabled in BBMULTICONFIG?" % (mcdep, dep))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800718 if mc == frommc:
719 fn = taskData[mcdep].build_targets[pn][0]
720 newdep = '%s:%s' % (fn,deptask)
721 taskData[mc].taskentries[tid].tdepends.append(newdep)
722
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 for mc in taskData:
724 for tid in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600726 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
727 #runtid = build_tid(mc, fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500728
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600729 #logger.debug2("Processing %s,%s:%s", mc, fn, taskname)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600730
731 depends = set()
732 task_deps = self.dataCaches[mc].task_deps[taskfn]
733
734 self.runtaskentries[tid] = RunTaskEntry()
735
736 if fn in taskData[mc].failed_fns:
737 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500738
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800739 # We add multiconfig dependencies before processing internal task deps (tdepends)
740 if 'mcdepends' in task_deps and taskname in task_deps['mcdepends']:
741 add_mc_dependencies(mc, tid)
742
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500743 # Resolve task internal dependencies
744 #
745 # e.g. addtask before X after Y
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600746 for t in taskData[mc].taskentries[tid].tdepends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800747 (depmc, depfn, deptaskname, _) = split_tid_mcfn(t)
748 depends.add(build_tid(depmc, depfn, deptaskname))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749
750 # Resolve 'deptask' dependencies
751 #
752 # e.g. do_sometask[deptask] = "do_someothertask"
753 # (makes sure sometask runs after someothertask of all DEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600754 if 'deptask' in task_deps and taskname in task_deps['deptask']:
755 tasknames = task_deps['deptask'][taskname].split()
756 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500757
758 # Resolve 'rdeptask' dependencies
759 #
760 # e.g. do_sometask[rdeptask] = "do_someothertask"
761 # (makes sure sometask runs after someothertask of all RDEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600762 if 'rdeptask' in task_deps and taskname in task_deps['rdeptask']:
763 tasknames = task_deps['rdeptask'][taskname].split()
764 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500765
766 # Resolve inter-task dependencies
767 #
768 # e.g. do_sometask[depends] = "targetname:do_someothertask"
769 # (makes sure sometask runs after targetname's someothertask)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600770 idepends = taskData[mc].taskentries[tid].idepends
771 for (depname, idependtask) in idepends:
772 if depname in taskData[mc].build_targets and taskData[mc].build_targets[depname] and not depname in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500773 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600774 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600776 t = depdata + ":" + idependtask
777 depends.add(t)
778 if t not in taskData[mc].taskentries:
779 bb.msg.fatal("RunQueue", "Task %s in %s depends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
780 irdepends = taskData[mc].taskentries[tid].irdepends
781 for (depname, idependtask) in irdepends:
782 if depname in taskData[mc].run_targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500783 # Won't be in run_targets if ASSUME_PROVIDED
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500784 if not taskData[mc].run_targets[depname]:
785 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600786 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600788 t = depdata + ":" + idependtask
789 depends.add(t)
790 if t not in taskData[mc].taskentries:
791 bb.msg.fatal("RunQueue", "Task %s in %s rdepends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500792
793 # Resolve recursive 'recrdeptask' dependencies (Part A)
794 #
795 # e.g. do_sometask[recrdeptask] = "do_someothertask"
796 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
797 # We cover the recursive part of the dependencies below
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 if 'recrdeptask' in task_deps and taskname in task_deps['recrdeptask']:
799 tasknames = task_deps['recrdeptask'][taskname].split()
800 recursivetasks[tid] = tasknames
801 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
802 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
803 if taskname in tasknames:
804 recursivetasksselfref.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500805
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600806 if 'recideptask' in task_deps and taskname in task_deps['recideptask']:
807 recursiveitasks[tid] = []
808 for t in task_deps['recideptask'][taskname].split():
809 newdep = build_tid(mc, fn, t)
810 recursiveitasks[tid].append(newdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500811
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600812 self.runtaskentries[tid].depends = depends
Brad Bishop316dfdd2018-06-25 12:45:53 -0400813 # Remove all self references
814 self.runtaskentries[tid].depends.discard(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500815
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600816 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500817
Brad Bishop316dfdd2018-06-25 12:45:53 -0400818 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600819 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400820
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821 # Resolve recursive 'recrdeptask' dependencies (Part B)
822 #
823 # e.g. do_sometask[recrdeptask] = "do_someothertask"
824 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600825 # We need to do this separately since we need all of runtaskentries[*].depends to be complete before this is processed
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600826
Brad Bishop316dfdd2018-06-25 12:45:53 -0400827 # Generating/interating recursive lists of dependencies is painful and potentially slow
828 # Precompute recursive task dependencies here by:
829 # a) create a temp list of reverse dependencies (revdeps)
830 # b) walk up the ends of the chains (when a given task no longer has dependencies i.e. len(deps) == 0)
831 # c) combine the total list of dependencies in cumulativedeps
832 # d) optimise by pre-truncating 'task' off the items in cumulativedeps (keeps items in sets lower)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834
Brad Bishop316dfdd2018-06-25 12:45:53 -0400835 revdeps = {}
836 deps = {}
837 cumulativedeps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600838 for tid in self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400839 deps[tid] = set(self.runtaskentries[tid].depends)
840 revdeps[tid] = set()
841 cumulativedeps[tid] = set()
842 # Generate a temp list of reverse dependencies
843 for tid in self.runtaskentries:
844 for dep in self.runtaskentries[tid].depends:
845 revdeps[dep].add(tid)
846 # Find the dependency chain endpoints
847 endpoints = set()
848 for tid in self.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +0000849 if not deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400850 endpoints.add(tid)
851 # Iterate the chains collating dependencies
852 while endpoints:
853 next = set()
854 for tid in endpoints:
855 for dep in revdeps[tid]:
856 cumulativedeps[dep].add(fn_from_tid(tid))
857 cumulativedeps[dep].update(cumulativedeps[tid])
858 if tid in deps[dep]:
859 deps[dep].remove(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +0000860 if not deps[dep]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400861 next.add(dep)
862 endpoints = next
863 #for tid in deps:
Andrew Geissler595f6302022-01-24 19:11:47 +0000864 # if deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400865 # bb.warn("Sanity test failure, dependencies left for %s (%s)" % (tid, deps[tid]))
866
867 # Loop here since recrdeptasks can depend upon other recrdeptasks and we have to
868 # resolve these recursively until we aren't adding any further extra dependencies
869 extradeps = True
870 while extradeps:
871 extradeps = 0
872 for tid in recursivetasks:
873 tasknames = recursivetasks[tid]
874
875 totaldeps = set(self.runtaskentries[tid].depends)
876 if tid in recursiveitasks:
877 totaldeps.update(recursiveitasks[tid])
878 for dep in recursiveitasks[tid]:
879 if dep not in self.runtaskentries:
880 continue
881 totaldeps.update(self.runtaskentries[dep].depends)
882
883 deps = set()
884 for dep in totaldeps:
885 if dep in cumulativedeps:
886 deps.update(cumulativedeps[dep])
887
888 for t in deps:
889 for taskname in tasknames:
890 newtid = t + ":" + taskname
891 if newtid == tid:
892 continue
893 if newtid in self.runtaskentries and newtid not in self.runtaskentries[tid].depends:
894 extradeps += 1
895 self.runtaskentries[tid].depends.add(newtid)
896
897 # Handle recursive tasks which depend upon other recursive tasks
898 deps = set()
899 for dep in self.runtaskentries[tid].depends.intersection(recursivetasks):
900 deps.update(self.runtaskentries[dep].depends.difference(self.runtaskentries[tid].depends))
901 for newtid in deps:
902 for taskname in tasknames:
903 if not newtid.endswith(":" + taskname):
904 continue
905 if newtid in self.runtaskentries:
906 extradeps += 1
907 self.runtaskentries[tid].depends.add(newtid)
908
909 bb.debug(1, "Added %s recursive dependencies in this loop" % extradeps)
910
911 # Remove recrdeptask circular references so that do_a[recrdeptask] = "do_a do_b" can work
912 for tid in recursivetasksselfref:
913 self.runtaskentries[tid].depends.difference_update(recursivetasksselfref)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600914
915 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600916 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600917
918 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500919
920 # Step B - Mark all active tasks
921 #
922 # Start with the tasks we were asked to run and mark all dependencies
923 # as active too. If the task is to be 'forced', clear its stamp. Once
924 # all active tasks are marked, prune the ones we don't need.
925
926 logger.verbose("Marking Active Tasks")
927
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600928 def mark_active(tid, depth):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500929 """
930 Mark an item as active along with its depends
931 (calls itself recursively)
932 """
933
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600934 if tid in runq_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500935 return
936
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600937 runq_build[tid] = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500938
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600939 depends = self.runtaskentries[tid].depends
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500940 for depend in depends:
941 mark_active(depend, depth+1)
942
Brad Bishop79641f22019-09-10 07:20:22 -0400943 def invalidate_task(tid, error_nostamp):
944 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
945 taskdep = self.dataCaches[mc].task_deps[taskfn]
946 if fn + ":" + taskname not in taskData[mc].taskentries:
947 logger.warning("Task %s does not exist, invalidating this task will have no effect" % taskname)
948 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
949 if error_nostamp:
950 bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
951 else:
952 bb.debug(1, "Task %s is marked nostamp, cannot invalidate this task" % taskname)
953 else:
954 logger.verbose("Invalidate task %s, %s", taskname, fn)
Andrew Geissler517393d2023-01-13 08:55:19 -0600955 bb.parse.siggen.invalidate_task(taskname, taskfn)
Brad Bishop79641f22019-09-10 07:20:22 -0400956
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600957 self.target_tids = []
958 for (mc, target, task, fn) in self.targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500959
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600960 if target not in taskData[mc].build_targets or not taskData[mc].build_targets[target]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500961 continue
962
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600963 if target in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500964 continue
965
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500966 parents = False
967 if task.endswith('-'):
968 parents = True
969 task = task[:-1]
970
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600971 if fn in taskData[mc].failed_fns:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500972 continue
973
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600974 # fn already has mc prefix
975 tid = fn + ":" + task
976 self.target_tids.append(tid)
977 if tid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500978 import difflib
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600979 tasks = []
980 for x in taskData[mc].taskentries:
981 if x.startswith(fn + ":"):
982 tasks.append(taskname_from_tid(x))
983 close_matches = difflib.get_close_matches(task, tasks, cutoff=0.7)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500984 if close_matches:
985 extra = ". Close matches:\n %s" % "\n ".join(close_matches)
986 else:
987 extra = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600988 bb.msg.fatal("RunQueue", "Task %s does not exist for target %s (%s)%s" % (task, target, tid, extra))
989
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500990 # For tasks called "XXXX-", ony run their dependencies
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500991 if parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600992 for i in self.runtaskentries[tid].depends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500993 mark_active(i, 1)
994 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600995 mark_active(tid, 1)
996
997 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600998 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500999
1000 # Step C - Prune all inactive tasks
1001 #
1002 # Once all active tasks are marked, prune the ones we don't need.
1003
Brad Bishop316dfdd2018-06-25 12:45:53 -04001004 # Handle --runall
1005 if self.cooker.configuration.runall:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001006 # re-run the mark_active and then drop unused tasks from new list
Andrew Geissler595f6302022-01-24 19:11:47 +00001007 reduced_tasklist = set(self.runtaskentries.keys())
1008 for tid in list(self.runtaskentries.keys()):
1009 if tid not in runq_build:
1010 reduced_tasklist.remove(tid)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001011 runq_build = {}
Brad Bishop316dfdd2018-06-25 12:45:53 -04001012
1013 for task in self.cooker.configuration.runall:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001014 if not task.startswith("do_"):
1015 task = "do_{0}".format(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001016 runall_tids = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00001017 for tid in reduced_tasklist:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001018 wanttid = "{0}:{1}".format(fn_from_tid(tid), task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001019 if wanttid in self.runtaskentries:
1020 runall_tids.add(wanttid)
1021
1022 for tid in list(runall_tids):
Andrew Geissler595f6302022-01-24 19:11:47 +00001023 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001024 if self.cooker.configuration.force:
1025 invalidate_task(tid, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001026
Andrew Geissler595f6302022-01-24 19:11:47 +00001027 delcount = set()
1028 for tid in list(self.runtaskentries.keys()):
1029 if tid not in runq_build:
1030 delcount.add(tid)
1031 del self.runtaskentries[tid]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001032
Andrew Geissler595f6302022-01-24 19:11:47 +00001033 if self.cooker.configuration.runall:
1034 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001035 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)))
1036
1037 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001038 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001039
1040 # Handle runonly
1041 if self.cooker.configuration.runonly:
1042 # re-run the mark_active and then drop unused tasks from new list
1043 runq_build = {}
1044
1045 for task in self.cooker.configuration.runonly:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001046 if not task.startswith("do_"):
1047 task = "do_{0}".format(task)
Andrew Geissler595f6302022-01-24 19:11:47 +00001048 runonly_tids = [k for k in self.runtaskentries.keys() if taskname_from_tid(k) == task]
Brad Bishop316dfdd2018-06-25 12:45:53 -04001049
Andrew Geissler595f6302022-01-24 19:11:47 +00001050 for tid in runonly_tids:
1051 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001052 if self.cooker.configuration.force:
1053 invalidate_task(tid, False)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001054
1055 for tid in list(self.runtaskentries.keys()):
1056 if tid not in runq_build:
Andrew Geissler595f6302022-01-24 19:11:47 +00001057 delcount.add(tid)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001058 del self.runtaskentries[tid]
1059
Andrew Geissler595f6302022-01-24 19:11:47 +00001060 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001061 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 -05001062
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001063 #
1064 # Step D - Sanity checks and computation
1065 #
1066
1067 # Check to make sure we still have tasks to run
Andrew Geissler595f6302022-01-24 19:11:47 +00001068 if not self.runtaskentries:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001069 if not taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001070 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.")
1071 else:
1072 bb.msg.fatal("RunQueue", "No active tasks and not in --continue mode?! Please report this bug.")
1073
Brad Bishop316dfdd2018-06-25 12:45:53 -04001074 logger.verbose("Pruned %s inactive tasks, %s left", len(delcount), len(self.runtaskentries))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001075
1076 logger.verbose("Assign Weightings")
1077
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001078 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001079 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001080
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001081 # Generate a list of reverse dependencies to ease future calculations
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001082 for tid in self.runtaskentries:
1083 for dep in self.runtaskentries[tid].depends:
1084 self.runtaskentries[dep].revdeps.add(tid)
1085
1086 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001087 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001088
1089 # Identify tasks at the end of dependency chains
1090 # Error on circular dependency loops (length two)
1091 endpoints = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001092 for tid in self.runtaskentries:
1093 revdeps = self.runtaskentries[tid].revdeps
Andrew Geissler595f6302022-01-24 19:11:47 +00001094 if not revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001095 endpoints.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001096 for dep in revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001097 if dep in self.runtaskentries[tid].depends:
1098 bb.msg.fatal("RunQueue", "Task %s has circular dependency on %s" % (tid, dep))
1099
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001100
1101 logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
1102
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001103 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001104 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001105
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001106 # Calculate task weights
1107 # Check of higher length circular dependencies
1108 self.runq_weight = self.calculate_task_weights(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 # Sanity Check - Check for multiple tasks building the same provider
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001114 for mc in self.dataCaches:
1115 prov_list = {}
1116 seen_fn = []
1117 for tid in self.runtaskentries:
1118 (tidmc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1119 if taskfn in seen_fn:
1120 continue
1121 if mc != tidmc:
1122 continue
1123 seen_fn.append(taskfn)
1124 for prov in self.dataCaches[mc].fn_provides[taskfn]:
1125 if prov not in prov_list:
1126 prov_list[prov] = [taskfn]
1127 elif taskfn not in prov_list[prov]:
1128 prov_list[prov].append(taskfn)
1129 for prov in prov_list:
1130 if len(prov_list[prov]) < 2:
1131 continue
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001132 if prov in self.multi_provider_allowed:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001133 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001134 seen_pn = []
1135 # If two versions of the same PN are being built its fatal, we don't support it.
1136 for fn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001137 pn = self.dataCaches[mc].pkg_fn[fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001138 if pn not in seen_pn:
1139 seen_pn.append(pn)
1140 else:
1141 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 +00001142 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 -05001143 #
1144 # Construct a list of things which uniquely depend on each provider
1145 # since this may help the user figure out which dependency is triggering this warning
1146 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001147 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 -05001148 deplist = {}
1149 commondeps = None
1150 for provfn in prov_list[prov]:
1151 deps = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001152 for tid in self.runtaskentries:
1153 fn = fn_from_tid(tid)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001154 if fn != provfn:
1155 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001156 for dep in self.runtaskentries[tid].revdeps:
1157 fn = fn_from_tid(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001158 if fn == provfn:
1159 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001160 deps.add(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001161 if not commondeps:
1162 commondeps = set(deps)
1163 else:
1164 commondeps &= deps
1165 deplist[provfn] = deps
1166 for provfn in deplist:
Andrew Geissler595f6302022-01-24 19:11:47 +00001167 msgs.append("\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001168 #
1169 # Construct a list of provides and runtime providers for each recipe
1170 # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC)
1171 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001172 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 -05001173 provide_results = {}
1174 rprovide_results = {}
1175 commonprovs = None
1176 commonrprovs = None
1177 for provfn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001178 provides = set(self.dataCaches[mc].fn_provides[provfn])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001179 rprovides = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001180 for rprovide in self.dataCaches[mc].rproviders:
1181 if provfn in self.dataCaches[mc].rproviders[rprovide]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001182 rprovides.add(rprovide)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001183 for package in self.dataCaches[mc].packages:
1184 if provfn in self.dataCaches[mc].packages[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001185 rprovides.add(package)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001186 for package in self.dataCaches[mc].packages_dynamic:
1187 if provfn in self.dataCaches[mc].packages_dynamic[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001188 rprovides.add(package)
1189 if not commonprovs:
1190 commonprovs = set(provides)
1191 else:
1192 commonprovs &= provides
1193 provide_results[provfn] = provides
1194 if not commonrprovs:
1195 commonrprovs = set(rprovides)
1196 else:
1197 commonrprovs &= rprovides
1198 rprovide_results[provfn] = rprovides
Andrew Geissler595f6302022-01-24 19:11:47 +00001199 #msgs.append("\nCommon provides:\n %s" % ("\n ".join(commonprovs)))
1200 #msgs.append("\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001201 for provfn in prov_list[prov]:
Andrew Geissler595f6302022-01-24 19:11:47 +00001202 msgs.append("\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs)))
1203 msgs.append("\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001204
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001205 if self.warn_multi_bb:
Andrew Geissler595f6302022-01-24 19:11:47 +00001206 logger.verbnote("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001207 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00001208 logger.error("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001209
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001210 self.init_progress_reporter.next_stage()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001211 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001212 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001213
1214 # Iterate over the task list looking for tasks with a 'setscene' function
Andrew Geissler82c905d2020-04-13 13:39:40 -05001215 self.runq_setscene_tids = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001216 if not self.cooker.configuration.nosetscene:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001217 for tid in self.runtaskentries:
1218 (mc, fn, taskname, _) = split_tid_mcfn(tid)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001219 setscenetid = tid + "_setscene"
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001220 if setscenetid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001221 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -05001222 self.runq_setscene_tids.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001223
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001224 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001225 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001226
1227 # Invalidate task if force mode active
1228 if self.cooker.configuration.force:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001229 for tid in self.target_tids:
1230 invalidate_task(tid, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001231
1232 # Invalidate task if invalidate mode active
1233 if self.cooker.configuration.invalidate_stamp:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001234 for tid in self.target_tids:
1235 fn = fn_from_tid(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001236 for st in self.cooker.configuration.invalidate_stamp.split(','):
1237 if not st.startswith("do_"):
1238 st = "do_%s" % st
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001239 invalidate_task(fn + ":" + st, True)
1240
1241 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001242 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001243
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001244 # Create and print to the logs a virtual/xxxx -> PN (fn) table
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001245 for mc in taskData:
1246 virtmap = taskData[mc].get_providermap(prefix="virtual/")
1247 virtpnmap = {}
1248 for v in virtmap:
1249 virtpnmap[v] = self.dataCaches[mc].pkg_fn[virtmap[v]]
1250 bb.debug(2, "%s resolved to: %s (%s)" % (v, virtpnmap[v], virtmap[v]))
1251 if hasattr(bb.parse.siggen, "tasks_resolved"):
1252 bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
1253
1254 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001255 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001256
Brad Bishop00e122a2019-10-05 11:10:57 -04001257 bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
1258
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001259 # Iterate over the task list and call into the siggen code
1260 dealtwith = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001261 todeal = set(self.runtaskentries)
Andrew Geissler595f6302022-01-24 19:11:47 +00001262 while todeal:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001263 for tid in todeal.copy():
Andrew Geissler595f6302022-01-24 19:11:47 +00001264 if not (self.runtaskentries[tid].depends - dealtwith):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001265 dealtwith.add(tid)
1266 todeal.remove(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001267 self.prepare_task_hash(tid)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001268 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001269
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001270 bb.parse.siggen.writeout_file_checksum_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001271
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001272 #self.dump_data()
1273 return len(self.runtaskentries)
1274
Brad Bishop19323692019-04-05 15:28:33 -04001275 def prepare_task_hash(self, tid):
Andrew Geissler517393d2023-01-13 08:55:19 -06001276 bb.parse.siggen.prep_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
1277 self.runtaskentries[tid].hash = bb.parse.siggen.get_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
Brad Bishop08902b02019-08-20 09:16:51 -04001278 self.runtaskentries[tid].unihash = bb.parse.siggen.get_unihash(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001279
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001280 def dump_data(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001281 """
1282 Dump some debug information on the internal data structures
1283 """
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001284 logger.debug3("run_tasks:")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001285 for tid in self.runtaskentries:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001286 logger.debug3(" %s: %s Deps %s RevDeps %s", tid,
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001287 self.runtaskentries[tid].weight,
1288 self.runtaskentries[tid].depends,
1289 self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001290
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001291class RunQueueWorker():
1292 def __init__(self, process, pipe):
1293 self.process = process
1294 self.pipe = pipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001295
1296class RunQueue:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001297 def __init__(self, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001298
1299 self.cooker = cooker
1300 self.cfgData = cfgData
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001301 self.rqdata = RunQueueData(self, cooker, cfgData, dataCaches, taskData, targets)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001302
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001303 self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION") or None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001304 self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID") or None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001305
1306 self.state = runQueuePrepare
1307
1308 # For disk space monitor
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001309 # Invoked at regular time intervals via the bitbake heartbeat event
1310 # while the build is running. We generate a unique name for the handler
1311 # here, just in case that there ever is more than one RunQueue instance,
Brad Bishop96ff1982019-08-19 13:50:42 -04001312 # start the handler when reaching runQueueSceneInit, and stop it when
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001313 # done with the build.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001314 self.dm = monitordisk.diskMonitor(cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001315 self.dm_event_handler_name = '_bb_diskmonitor_' + str(id(self))
1316 self.dm_event_handler_registered = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001317 self.rqexe = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001318 self.worker = {}
1319 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001320
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001321 def _start_worker(self, mc, fakeroot = False, rqexec = None):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001322 logger.debug("Starting bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001323 magic = "decafbad"
1324 if self.cooker.configuration.profile:
1325 magic = "decafbadbad"
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001326 fakerootlogs = None
Andrew Geissler220dafd2023-10-04 10:18:08 -05001327
1328 workerscript = os.path.realpath(os.path.dirname(__file__) + "/../../bin/bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001329 if fakeroot:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001330 magic = magic + "beef"
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001331 mcdata = self.cooker.databuilder.mcdata[mc]
Brad Bishop19323692019-04-05 15:28:33 -04001332 fakerootcmd = shlex.split(mcdata.getVar("FAKEROOTCMD"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001333 fakerootenv = (mcdata.getVar("FAKEROOTBASEENV") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334 env = os.environ.copy()
1335 for key, value in (var.split('=') for var in fakerootenv):
1336 env[key] = value
Andrew Geissler220dafd2023-10-04 10:18:08 -05001337 worker = subprocess.Popen(fakerootcmd + [sys.executable, workerscript, magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001338 fakerootlogs = self.rqdata.dataCaches[mc].fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001339 else:
Andrew Geissler220dafd2023-10-04 10:18:08 -05001340 worker = subprocess.Popen([sys.executable, workerscript, magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001341 bb.utils.nonblockingfd(worker.stdout)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001342 workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, self, rqexec, fakerootlogs=fakerootlogs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001343
1344 workerdata = {
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001345 "sigdata" : bb.parse.siggen.get_taskdata(),
Andrew Geissler82c905d2020-04-13 13:39:40 -05001346 "logdefaultlevel" : bb.msg.loggerDefaultLogLevel,
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001347 "build_verbose_shell" : self.cooker.configuration.build_verbose_shell,
1348 "build_verbose_stdout" : self.cooker.configuration.build_verbose_stdout,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001349 "logdefaultdomain" : bb.msg.loggerDefaultDomains,
1350 "prhost" : self.cooker.prhost,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001351 "buildname" : self.cfgData.getVar("BUILDNAME"),
1352 "date" : self.cfgData.getVar("DATE"),
1353 "time" : self.cfgData.getVar("TIME"),
Brad Bishopa34c0302019-09-23 22:34:48 -04001354 "hashservaddr" : self.cooker.hashservaddr,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001355 "umask" : self.cfgData.getVar("BB_DEFAULT_UMASK"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001356 }
1357
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001358 worker.stdin.write(b"<cookerconfig>" + pickle.dumps(self.cooker.configuration) + b"</cookerconfig>")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001359 worker.stdin.write(b"<extraconfigdata>" + pickle.dumps(self.cooker.extraconfigdata) + b"</extraconfigdata>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001360 worker.stdin.write(b"<workerdata>" + pickle.dumps(workerdata) + b"</workerdata>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001361 worker.stdin.flush()
1362
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001363 return RunQueueWorker(worker, workerpipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001364
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001365 def _teardown_worker(self, worker):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001366 if not worker:
1367 return
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001368 logger.debug("Teardown for bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001369 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001370 worker.process.stdin.write(b"<quit></quit>")
1371 worker.process.stdin.flush()
1372 worker.process.stdin.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001373 except IOError:
1374 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001375 while worker.process.returncode is None:
1376 worker.pipe.read()
1377 worker.process.poll()
1378 while worker.pipe.read():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001379 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001380 worker.pipe.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001381
1382 def start_worker(self):
1383 if self.worker:
1384 self.teardown_workers()
1385 self.teardown = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001386 for mc in self.rqdata.dataCaches:
1387 self.worker[mc] = self._start_worker(mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001388
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001389 def start_fakeworker(self, rqexec, mc):
1390 if not mc in self.fakeworker:
1391 self.fakeworker[mc] = self._start_worker(mc, True, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001392
1393 def teardown_workers(self):
1394 self.teardown = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001395 for mc in self.worker:
1396 self._teardown_worker(self.worker[mc])
1397 self.worker = {}
1398 for mc in self.fakeworker:
1399 self._teardown_worker(self.fakeworker[mc])
1400 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001401
1402 def read_workers(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001403 for mc in self.worker:
1404 self.worker[mc].pipe.read()
1405 for mc in self.fakeworker:
1406 self.fakeworker[mc].pipe.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001407
1408 def active_fds(self):
1409 fds = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001410 for mc in self.worker:
1411 fds.append(self.worker[mc].pipe.input)
1412 for mc in self.fakeworker:
1413 fds.append(self.fakeworker[mc].pipe.input)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001414 return fds
1415
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001416 def check_stamp_task(self, tid, taskname = None, recurse = False, cache = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001417 def get_timestamp(f):
1418 try:
1419 if not os.access(f, os.F_OK):
1420 return None
1421 return os.stat(f)[stat.ST_MTIME]
1422 except:
1423 return None
1424
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001425 (mc, fn, tn, taskfn) = split_tid_mcfn(tid)
1426 if taskname is None:
1427 taskname = tn
1428
Andrew Geissler517393d2023-01-13 08:55:19 -06001429 stampfile = bb.parse.siggen.stampfile_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001430
1431 # If the stamp is missing, it's not current
1432 if not os.access(stampfile, os.F_OK):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001433 logger.debug2("Stampfile %s not available", stampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001434 return False
1435 # If it's a 'nostamp' task, it's not current
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001436 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001437 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001438 logger.debug2("%s.%s is nostamp\n", fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001439 return False
1440
Andrew Geissler517393d2023-01-13 08:55:19 -06001441 if taskname.endswith("_setscene"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001442 return True
1443
1444 if cache is None:
1445 cache = {}
1446
1447 iscurrent = True
1448 t1 = get_timestamp(stampfile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001449 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001450 if iscurrent:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001451 (mc2, fn2, taskname2, taskfn2) = split_tid_mcfn(dep)
Andrew Geissler517393d2023-01-13 08:55:19 -06001452 stampfile2 = bb.parse.siggen.stampfile_mcfn(taskname2, taskfn2)
1453 stampfile3 = bb.parse.siggen.stampfile_mcfn(taskname2 + "_setscene", taskfn2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001454 t2 = get_timestamp(stampfile2)
1455 t3 = get_timestamp(stampfile3)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001456 if t3 and not t2:
1457 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001458 if t3 and t3 > t2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001459 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00001460 if fn == fn2:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001461 if not t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001462 logger.debug2('Stampfile %s does not exist', stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001463 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001464 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001465 if t1 < t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001466 logger.debug2('Stampfile %s < %s', stampfile, stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001467 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001468 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001469 if recurse and iscurrent:
1470 if dep in cache:
1471 iscurrent = cache[dep]
1472 if not iscurrent:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001473 logger.debug2('Stampfile for dependency %s:%s invalid (cached)' % (fn2, taskname2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001474 else:
1475 iscurrent = self.check_stamp_task(dep, recurse=True, cache=cache)
1476 cache[dep] = iscurrent
1477 if recurse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001478 cache[tid] = iscurrent
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001479 return iscurrent
1480
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001481 def validate_hashes(self, tocheck, data, currentcount=0, siginfo=False, summary=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04001482 valid = set()
1483 if self.hashvalidate:
Brad Bishop08902b02019-08-20 09:16:51 -04001484 sq_data = {}
1485 sq_data['hash'] = {}
1486 sq_data['hashfn'] = {}
1487 sq_data['unihash'] = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04001488 for tid in tocheck:
1489 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04001490 sq_data['hash'][tid] = self.rqdata.runtaskentries[tid].hash
1491 sq_data['hashfn'][tid] = self.rqdata.dataCaches[mc].hashfn[taskfn]
1492 sq_data['unihash'][tid] = self.rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -04001493
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001494 valid = self.validate_hash(sq_data, data, siginfo, currentcount, summary)
Brad Bishop96ff1982019-08-19 13:50:42 -04001495
1496 return valid
1497
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001498 def validate_hash(self, sq_data, d, siginfo, currentcount, summary):
1499 locs = {"sq_data" : sq_data, "d" : d, "siginfo" : siginfo, "currentcount" : currentcount, "summary" : summary}
Brad Bishop19323692019-04-05 15:28:33 -04001500
Brad Bishop08902b02019-08-20 09:16:51 -04001501 # Metadata has **kwargs so args can be added, sq_data can also gain new fields
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001502 call = self.hashvalidate + "(sq_data, d, siginfo=siginfo, currentcount=currentcount, summary=summary)"
Brad Bishop19323692019-04-05 15:28:33 -04001503
Brad Bishop19323692019-04-05 15:28:33 -04001504 return bb.utils.better_eval(call, locs)
1505
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001506 def _execute_runqueue(self):
1507 """
1508 Run the tasks in a queue prepared by rqdata.prepare()
1509 Upon failure, optionally try to recover the build using any alternate providers
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001510 (if the halt on failure configuration option isn't set)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001511 """
1512
1513 retval = True
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001514 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001515
1516 if self.state is runQueuePrepare:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001517 # NOTE: if you add, remove or significantly refactor the stages of this
1518 # process then you should recalculate the weightings here. This is quite
1519 # easy to do - just change the next line temporarily to pass debug=True as
1520 # the last parameter and you'll get a printout of the weightings as well
1521 # as a map to the lines where next_stage() was called. Of course this isn't
1522 # critical, but it helps to keep the progress reporting accurate.
1523 self.rqdata.init_progress_reporter = bb.progress.MultiStageProcessProgressReporter(self.cooker.data,
1524 "Initialising tasks",
1525 [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 -05001526 if self.rqdata.prepare() == 0:
1527 self.state = runQueueComplete
1528 else:
1529 self.state = runQueueSceneInit
Brad Bishop00e122a2019-10-05 11:10:57 -04001530 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001531
1532 if self.state is runQueueSceneInit:
Brad Bishop96ff1982019-08-19 13:50:42 -04001533 self.rqdata.init_progress_reporter.next_stage()
1534
1535 # we are ready to run, emit dependency info to any UI or class which
1536 # needs it
1537 depgraph = self.cooker.buildDependTree(self, self.rqdata.taskData)
1538 self.rqdata.init_progress_reporter.next_stage()
1539 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
1540
Brad Bishope2d5b612018-11-23 10:55:50 +13001541 if not self.dm_event_handler_registered:
1542 res = bb.event.register(self.dm_event_handler_name,
Andrew Geissler517393d2023-01-13 08:55:19 -06001543 lambda x, y: self.dm.check(self) if self.state in [runQueueRunning, runQueueCleanUp] else False,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001544 ('bb.event.HeartbeatEvent',), data=self.cfgData)
Brad Bishope2d5b612018-11-23 10:55:50 +13001545 self.dm_event_handler_registered = True
1546
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001547 dump = self.cooker.configuration.dump_signatures
1548 if dump:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001549 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001550 if 'printdiff' in dump:
1551 invalidtasks = self.print_diffscenetasks()
1552 self.dump_signatures(dump)
1553 if 'printdiff' in dump:
1554 self.write_diffscenetasks(invalidtasks)
1555 self.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001556
Brad Bishop96ff1982019-08-19 13:50:42 -04001557 if self.state is runQueueSceneInit:
1558 self.rqdata.init_progress_reporter.next_stage()
1559 self.start_worker()
1560 self.rqdata.init_progress_reporter.next_stage()
1561 self.rqexe = RunQueueExecute(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001562
Brad Bishop96ff1982019-08-19 13:50:42 -04001563 # If we don't have any setscene functions, skip execution
Andrew Geissler595f6302022-01-24 19:11:47 +00001564 if not self.rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04001565 logger.info('No setscene tasks')
1566 for tid in self.rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00001567 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04001568 self.rqexe.setbuildable(tid)
1569 self.rqexe.tasks_notcovered.add(tid)
1570 self.rqexe.sqdone = True
1571 logger.info('Executing Tasks')
1572 self.state = runQueueRunning
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001573
1574 if self.state is runQueueRunning:
1575 retval = self.rqexe.execute()
1576
1577 if self.state is runQueueCleanUp:
1578 retval = self.rqexe.finish()
1579
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001580 build_done = self.state is runQueueComplete or self.state is runQueueFailed
1581
1582 if build_done and self.dm_event_handler_registered:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001583 bb.event.remove(self.dm_event_handler_name, None, data=self.cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001584 self.dm_event_handler_registered = False
1585
1586 if build_done and self.rqexe:
Brad Bishop08902b02019-08-20 09:16:51 -04001587 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001588 self.teardown_workers()
Brad Bishop96ff1982019-08-19 13:50:42 -04001589 if self.rqexe:
1590 if self.rqexe.stats.failed:
1591 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)
1592 else:
1593 # Let's avoid the word "failed" if nothing actually did
1594 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 -05001595
1596 if self.state is runQueueFailed:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001597 raise bb.runqueue.TaskFailure(self.rqexe.failed_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001598
1599 if self.state is runQueueComplete:
1600 # All done
1601 return False
1602
1603 # Loop
1604 return retval
1605
1606 def execute_runqueue(self):
1607 # Catch unexpected exceptions and ensure we exit when an error occurs, not loop.
1608 try:
1609 return self._execute_runqueue()
1610 except bb.runqueue.TaskFailure:
1611 raise
1612 except SystemExit:
1613 raise
1614 except bb.BBHandledException:
1615 try:
1616 self.teardown_workers()
1617 except:
1618 pass
1619 self.state = runQueueComplete
1620 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001621 except Exception as err:
1622 logger.exception("An uncaught exception occurred in runqueue")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001623 try:
1624 self.teardown_workers()
1625 except:
1626 pass
1627 self.state = runQueueComplete
1628 raise
1629
1630 def finish_runqueue(self, now = False):
1631 if not self.rqexe:
1632 self.state = runQueueComplete
1633 return
1634
1635 if now:
1636 self.rqexe.finish_now()
1637 else:
1638 self.rqexe.finish()
1639
Andrew Geissler517393d2023-01-13 08:55:19 -06001640 def _rq_dump_sigtid(self, tids):
1641 for tid in tids:
1642 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1643 dataCaches = self.rqdata.dataCaches
1644 bb.parse.siggen.dump_sigtask(taskfn, taskname, dataCaches[mc].stamp[taskfn], True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001645
1646 def dump_signatures(self, options):
Andrew Geissler517393d2023-01-13 08:55:19 -06001647 if bb.cooker.CookerFeatures.RECIPE_SIGGEN_INFO not in self.cooker.featureset:
1648 bb.fatal("The dump signatures functionality needs the RECIPE_SIGGEN_INFO feature enabled")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001649
Andrew Geissler517393d2023-01-13 08:55:19 -06001650 bb.note("Writing task signature files")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001651
1652 max_process = int(self.cfgData.getVar("BB_NUMBER_PARSE_THREADS") or os.cpu_count() or 1)
Andrew Geissler517393d2023-01-13 08:55:19 -06001653 def chunkify(l, n):
1654 return [l[i::n] for i in range(n)]
1655 tids = chunkify(list(self.rqdata.runtaskentries), max_process)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001656 # We cannot use the real multiprocessing.Pool easily due to some local data
1657 # that can't be pickled. This is a cheap multi-process solution.
1658 launched = []
Andrew Geissler517393d2023-01-13 08:55:19 -06001659 while tids:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001660 if len(launched) < max_process:
Andrew Geissler517393d2023-01-13 08:55:19 -06001661 p = Process(target=self._rq_dump_sigtid, args=(tids.pop(), ))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001662 p.start()
1663 launched.append(p)
1664 for q in launched:
1665 # The finished processes are joined when calling is_alive()
1666 if not q.is_alive():
1667 launched.remove(q)
1668 for p in launched:
1669 p.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001670
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001671 bb.parse.siggen.dump_sigs(self.rqdata.dataCaches, options)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001672
1673 return
1674
1675 def print_diffscenetasks(self):
1676
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001677 noexec = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001678 tocheck = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001679
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001680 for tid in self.rqdata.runtaskentries:
1681 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1682 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001683
1684 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001685 noexec.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001686 continue
1687
Brad Bishop96ff1982019-08-19 13:50:42 -04001688 tocheck.add(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001689
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001690 valid_new = self.validate_hashes(tocheck, self.cooker.data, 0, True, summary=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001691
1692 # Tasks which are both setscene and noexec never care about dependencies
1693 # We therefore find tasks which are setscene and noexec and mark their
1694 # unique dependencies as valid.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001695 for tid in noexec:
1696 if tid not in self.rqdata.runq_setscene_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001697 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001698 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001699 hasnoexecparents = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001700 for dep2 in self.rqdata.runtaskentries[dep].revdeps:
1701 if dep2 in self.rqdata.runq_setscene_tids and dep2 in noexec:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001702 continue
1703 hasnoexecparents = False
1704 break
1705 if hasnoexecparents:
1706 valid_new.add(dep)
1707
1708 invalidtasks = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001709 for tid in self.rqdata.runtaskentries:
1710 if tid not in valid_new and tid not in noexec:
1711 invalidtasks.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001712
1713 found = set()
1714 processed = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001715 for tid in invalidtasks:
1716 toprocess = set([tid])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001717 while toprocess:
1718 next = set()
1719 for t in toprocess:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001720 for dep in self.rqdata.runtaskentries[t].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001721 if dep in invalidtasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001722 found.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001723 if dep not in processed:
1724 processed.add(dep)
1725 next.add(dep)
1726 toprocess = next
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001727 if tid in found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001728 toprocess = set()
1729
1730 tasklist = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001731 for tid in invalidtasks.difference(found):
1732 tasklist.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001733
1734 if tasklist:
1735 bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
1736
1737 return invalidtasks.difference(found)
1738
1739 def write_diffscenetasks(self, invalidtasks):
1740
1741 # Define recursion callback
1742 def recursecb(key, hash1, hash2):
1743 hashes = [hash1, hash2]
1744 hashfiles = bb.siggen.find_siginfo(key, None, hashes, self.cfgData)
1745
1746 recout = []
1747 if len(hashfiles) == 2:
1748 out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
Brad Bishopc342db32019-05-15 21:57:59 -04001749 recout.extend(list(' ' + l for l in out2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001750 else:
1751 recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
1752
1753 return recout
1754
1755
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001756 for tid in invalidtasks:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001757 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1758 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001759 h = self.rqdata.runtaskentries[tid].hash
Patrick Williams03907ee2022-05-01 06:28:52 -05001760 matches = bb.siggen.find_siginfo(pn, taskname, [], self.cooker.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001761 match = None
1762 for m in matches:
1763 if h in m:
1764 match = m
1765 if match is None:
1766 bb.fatal("Can't find a task we're supposed to have written out? (hash: %s)?" % h)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001767 matches = {k : v for k, v in iter(matches.items()) if h not in k}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001768 if matches:
1769 latestmatch = sorted(matches.keys(), key=lambda f: matches[f])[-1]
Brad Bishop19323692019-04-05 15:28:33 -04001770 prevh = __find_sha256__.search(latestmatch).group(0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001771 output = bb.siggen.compare_sigfiles(latestmatch, match, recursecb)
1772 bb.plain("\nTask %s:%s couldn't be used from the cache because:\n We need hash %s, closest matching task was %s\n " % (pn, taskname, h, prevh) + '\n '.join(output))
1773
Brad Bishop96ff1982019-08-19 13:50:42 -04001774
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001775class RunQueueExecute:
1776
1777 def __init__(self, rq):
1778 self.rq = rq
1779 self.cooker = rq.cooker
1780 self.cfgData = rq.cfgData
1781 self.rqdata = rq.rqdata
1782
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001783 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
1784 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001785 self.max_cpu_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_CPU")
1786 self.max_io_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_IO")
Patrick Williams92b42cb2022-09-03 06:53:57 -05001787 self.max_memory_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_MEMORY")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001788
Brad Bishop96ff1982019-08-19 13:50:42 -04001789 self.sq_buildable = set()
1790 self.sq_running = set()
1791 self.sq_live = set()
1792
Brad Bishop08902b02019-08-20 09:16:51 -04001793 self.updated_taskhash_queue = []
1794 self.pending_migrations = set()
1795
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001796 self.runq_buildable = set()
1797 self.runq_running = set()
1798 self.runq_complete = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001799 self.runq_tasksrun = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001800
1801 self.build_stamps = {}
1802 self.build_stamps2 = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001803 self.failed_tids = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001804 self.sq_deferred = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001805
1806 self.stampcache = {}
1807
Brad Bishop08902b02019-08-20 09:16:51 -04001808 self.holdoff_tasks = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04001809 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04001810 self.sqdone = False
1811
Andrew Geissler5199d832021-09-24 16:47:35 -05001812 self.stats = RunQueueStats(len(self.rqdata.runtaskentries), len(self.rqdata.runq_setscene_tids))
Brad Bishop96ff1982019-08-19 13:50:42 -04001813
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001814 for mc in rq.worker:
1815 rq.worker[mc].pipe.setrunqueueexec(self)
1816 for mc in rq.fakeworker:
1817 rq.fakeworker[mc].pipe.setrunqueueexec(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001818
1819 if self.number_tasks <= 0:
1820 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks)
1821
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001822 lower_limit = 1.0
1823 upper_limit = 1000000.0
1824 if self.max_cpu_pressure:
1825 self.max_cpu_pressure = float(self.max_cpu_pressure)
1826 if self.max_cpu_pressure < lower_limit:
1827 bb.fatal("Invalid BB_PRESSURE_MAX_CPU %s, minimum value is %s." % (self.max_cpu_pressure, lower_limit))
1828 if self.max_cpu_pressure > upper_limit:
1829 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))
1830
1831 if self.max_io_pressure:
1832 self.max_io_pressure = float(self.max_io_pressure)
1833 if self.max_io_pressure < lower_limit:
1834 bb.fatal("Invalid BB_PRESSURE_MAX_IO %s, minimum value is %s." % (self.max_io_pressure, lower_limit))
1835 if self.max_io_pressure > upper_limit:
1836 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))
1837
Patrick Williams92b42cb2022-09-03 06:53:57 -05001838 if self.max_memory_pressure:
1839 self.max_memory_pressure = float(self.max_memory_pressure)
1840 if self.max_memory_pressure < lower_limit:
1841 bb.fatal("Invalid BB_PRESSURE_MAX_MEMORY %s, minimum value is %s." % (self.max_memory_pressure, lower_limit))
1842 if self.max_memory_pressure > upper_limit:
1843 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))
1844
Brad Bishop96ff1982019-08-19 13:50:42 -04001845 # List of setscene tasks which we've covered
1846 self.scenequeue_covered = set()
1847 # List of tasks which are covered (including setscene ones)
1848 self.tasks_covered = set()
1849 self.tasks_scenequeue_done = set()
1850 self.scenequeue_notcovered = set()
1851 self.tasks_notcovered = set()
1852 self.scenequeue_notneeded = set()
1853
Brad Bishop08902b02019-08-20 09:16:51 -04001854 # We can't skip specified target tasks which aren't setscene tasks
1855 self.cantskip = set(self.rqdata.target_tids)
1856 self.cantskip.difference_update(self.rqdata.runq_setscene_tids)
1857 self.cantskip.intersection_update(self.rqdata.runtaskentries)
Brad Bishop96ff1982019-08-19 13:50:42 -04001858
1859 schedulers = self.get_schedulers()
1860 for scheduler in schedulers:
1861 if self.scheduler == scheduler.name:
1862 self.sched = scheduler(self, self.rqdata)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001863 logger.debug("Using runqueue scheduler '%s'", scheduler.name)
Brad Bishop96ff1982019-08-19 13:50:42 -04001864 break
1865 else:
1866 bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
1867 (self.scheduler, ", ".join(obj.name for obj in schedulers)))
1868
Andrew Geissler595f6302022-01-24 19:11:47 +00001869 #if self.rqdata.runq_setscene_tids:
Brad Bishop08902b02019-08-20 09:16:51 -04001870 self.sqdata = SQData()
1871 build_scenequeue_data(self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self)
Brad Bishop96ff1982019-08-19 13:50:42 -04001872
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001873 def runqueue_process_waitpid(self, task, status, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001874
1875 # self.build_stamps[pid] may not exist when use shared work directory.
1876 if task in self.build_stamps:
1877 self.build_stamps2.remove(self.build_stamps[task])
1878 del self.build_stamps[task]
1879
Brad Bishop96ff1982019-08-19 13:50:42 -04001880 if task in self.sq_live:
1881 if status != 0:
1882 self.sq_task_fail(task, status)
1883 else:
1884 self.sq_task_complete(task)
1885 self.sq_live.remove(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05001886 self.stats.updateActiveSetscene(len(self.sq_live))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001887 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04001888 if status != 0:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001889 self.task_fail(task, status, fakerootlog=fakerootlog)
Brad Bishop96ff1982019-08-19 13:50:42 -04001890 else:
1891 self.task_complete(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001892 return True
1893
1894 def finish_now(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001895 for mc in self.rq.worker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001896 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001897 self.rq.worker[mc].process.stdin.write(b"<finishnow></finishnow>")
1898 self.rq.worker[mc].process.stdin.flush()
1899 except IOError:
1900 # worker must have died?
1901 pass
1902 for mc in self.rq.fakeworker:
1903 try:
1904 self.rq.fakeworker[mc].process.stdin.write(b"<finishnow></finishnow>")
1905 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001906 except IOError:
1907 # worker must have died?
1908 pass
1909
Andrew Geissler595f6302022-01-24 19:11:47 +00001910 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001911 self.rq.state = runQueueFailed
1912 return
1913
1914 self.rq.state = runQueueComplete
1915 return
1916
1917 def finish(self):
1918 self.rq.state = runQueueCleanUp
1919
Andrew Geissler5199d832021-09-24 16:47:35 -05001920 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001921 if active > 0:
1922 bb.event.fire(runQueueExitWait(active), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001923 self.rq.read_workers()
1924 return self.rq.active_fds()
1925
Andrew Geissler595f6302022-01-24 19:11:47 +00001926 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001927 self.rq.state = runQueueFailed
1928 return True
1929
1930 self.rq.state = runQueueComplete
1931 return True
1932
Brad Bishop96ff1982019-08-19 13:50:42 -04001933 # Used by setscene only
1934 def check_dependencies(self, task, taskdeps):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001935 if not self.rq.depvalidate:
1936 return False
1937
Brad Bishop08902b02019-08-20 09:16:51 -04001938 # Must not edit parent data
1939 taskdeps = set(taskdeps)
1940
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001941 taskdata = {}
1942 taskdeps.add(task)
1943 for dep in taskdeps:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001944 (mc, fn, taskname, taskfn) = split_tid_mcfn(dep)
1945 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001946 taskdata[dep] = [pn, taskname, fn]
1947 call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001948 locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001949 valid = bb.utils.better_eval(call, locs)
1950 return valid
1951
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001952 def can_start_task(self):
Andrew Geissler5199d832021-09-24 16:47:35 -05001953 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001954 can_start = active < self.number_tasks
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001955 return can_start
1956
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001957 def get_schedulers(self):
1958 schedulers = set(obj for obj in globals().values()
1959 if type(obj) is type and
1960 issubclass(obj, RunQueueScheduler))
1961
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001962 user_schedulers = self.cfgData.getVar("BB_SCHEDULERS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001963 if user_schedulers:
1964 for sched in user_schedulers.split():
1965 if not "." in sched:
1966 bb.note("Ignoring scheduler '%s' from BB_SCHEDULERS: not an import" % sched)
1967 continue
1968
1969 modname, name = sched.rsplit(".", 1)
1970 try:
1971 module = __import__(modname, fromlist=(name,))
1972 except ImportError as exc:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001973 bb.fatal("Unable to import scheduler '%s' from '%s': %s" % (name, modname, exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001974 else:
1975 schedulers.add(getattr(module, name))
1976 return schedulers
1977
1978 def setbuildable(self, task):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001979 self.runq_buildable.add(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001980 self.sched.newbuildable(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001981
1982 def task_completeoutright(self, task):
1983 """
1984 Mark a task as completed
1985 Look at the reverse dependencies and mark any task with
1986 completed dependencies as buildable
1987 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001988 self.runq_complete.add(task)
1989 for revdep in self.rqdata.runtaskentries[task].revdeps:
1990 if revdep in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001991 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001992 if revdep in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001993 continue
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001994 alldeps = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001995 for dep in self.rqdata.runtaskentries[revdep].depends:
1996 if dep not in self.runq_complete:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001997 alldeps = False
1998 break
1999 if alldeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002000 self.setbuildable(revdep)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002001 logger.debug("Marking task %s as buildable", revdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002002
Andrew Geissler8f840682023-07-21 09:09:43 -05002003 found = None
2004 for t in sorted(self.sq_deferred.copy()):
Andrew Geissler5199d832021-09-24 16:47:35 -05002005 if self.sq_deferred[t] == task:
Andrew Geissler8f840682023-07-21 09:09:43 -05002006 # Allow the next deferred task to run. Any other deferred tasks should be deferred after that task.
2007 # We shouldn't allow all to run at once as it is prone to races.
2008 if not found:
2009 bb.debug(1, "Deferred task %s now buildable" % t)
2010 del self.sq_deferred[t]
2011 update_scenequeue_data([t], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
2012 found = t
2013 else:
2014 bb.debug(1, "Deferring %s after %s" % (t, found))
2015 self.sq_deferred[t] = found
Andrew Geissler5199d832021-09-24 16:47:35 -05002016
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002017 def task_complete(self, task):
2018 self.stats.taskCompleted()
2019 bb.event.fire(runQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
2020 self.task_completeoutright(task)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002021 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002022
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002023 def task_fail(self, task, exitcode, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002024 """
2025 Called when a task has failed
2026 Updates the state engine with the failure
2027 """
2028 self.stats.taskFailed()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002029 self.failed_tids.append(task)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002030
Andrew Geissler595f6302022-01-24 19:11:47 +00002031 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002032 if fakerootlog and os.path.exists(fakerootlog):
2033 with open(fakerootlog) as fakeroot_log_file:
2034 fakeroot_failed = False
2035 for line in reversed(fakeroot_log_file.readlines()):
2036 for fakeroot_error in ['mismatch', 'error', 'fatal']:
2037 if fakeroot_error in line.lower():
2038 fakeroot_failed = True
2039 if 'doing new pid setup and server start' in line:
2040 break
Andrew Geissler595f6302022-01-24 19:11:47 +00002041 fakeroot_log.append(line)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002042
2043 if not fakeroot_failed:
Andrew Geissler595f6302022-01-24 19:11:47 +00002044 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002045
Andrew Geissler595f6302022-01-24 19:11:47 +00002046 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 -05002047
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002048 if self.rqdata.taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002049 self.rq.state = runQueueCleanUp
2050
2051 def task_skip(self, task, reason):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002052 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002053 self.setbuildable(task)
2054 bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
2055 self.task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002056 self.stats.taskSkipped()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002057 self.stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002058
Brad Bishop08902b02019-08-20 09:16:51 -04002059 def summarise_scenequeue_errors(self):
2060 err = False
2061 if not self.sqdone:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002062 logger.debug('We could skip tasks %s', "\n".join(sorted(self.scenequeue_covered)))
Andrew Geissler5199d832021-09-24 16:47:35 -05002063 completeevent = sceneQueueComplete(self.stats, self.rq)
Brad Bishop08902b02019-08-20 09:16:51 -04002064 bb.event.fire(completeevent, self.cfgData)
2065 if self.sq_deferred:
2066 logger.error("Scenequeue had deferred entries: %s" % pprint.pformat(self.sq_deferred))
2067 err = True
2068 if self.updated_taskhash_queue:
2069 logger.error("Scenequeue had unprocessed changed taskhash entries: %s" % pprint.pformat(self.updated_taskhash_queue))
2070 err = True
2071 if self.holdoff_tasks:
2072 logger.error("Scenequeue had holdoff tasks: %s" % pprint.pformat(self.holdoff_tasks))
2073 err = True
2074
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002075 for tid in self.scenequeue_covered.intersection(self.scenequeue_notcovered):
2076 # No task should end up in both covered and uncovered, that is a bug.
2077 logger.error("Setscene task %s in both covered and notcovered." % tid)
2078
Brad Bishop08902b02019-08-20 09:16:51 -04002079 for tid in self.rqdata.runq_setscene_tids:
2080 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2081 err = True
2082 logger.error("Setscene Task %s was never marked as covered or not covered" % tid)
2083 if tid not in self.sq_buildable:
2084 err = True
2085 logger.error("Setscene Task %s was never marked as buildable" % tid)
2086 if tid not in self.sq_running:
2087 err = True
2088 logger.error("Setscene Task %s was never marked as running" % tid)
2089
2090 for x in self.rqdata.runtaskentries:
2091 if x not in self.tasks_covered and x not in self.tasks_notcovered:
2092 logger.error("Task %s was never moved from the setscene queue" % x)
2093 err = True
2094 if x not in self.tasks_scenequeue_done:
2095 logger.error("Task %s was never processed by the setscene code" % x)
2096 err = True
Andrew Geissler595f6302022-01-24 19:11:47 +00002097 if not self.rqdata.runtaskentries[x].depends and x not in self.runq_buildable:
Brad Bishop08902b02019-08-20 09:16:51 -04002098 logger.error("Task %s was never marked as buildable by the setscene code" % x)
2099 err = True
2100 return err
2101
2102
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002103 def execute(self):
2104 """
Brad Bishop96ff1982019-08-19 13:50:42 -04002105 Run the tasks in a queue prepared by prepare_runqueue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002106 """
2107
2108 self.rq.read_workers()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002109 if self.updated_taskhash_queue or self.pending_migrations:
2110 self.process_possible_migrations()
2111
2112 if not hasattr(self, "sorted_setscene_tids"):
2113 # Don't want to sort this set every execution
2114 self.sorted_setscene_tids = sorted(self.rqdata.runq_setscene_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002115
Brad Bishop96ff1982019-08-19 13:50:42 -04002116 task = None
2117 if not self.sqdone and self.can_start_task():
2118 # Find the next setscene to run
Andrew Geissler82c905d2020-04-13 13:39:40 -05002119 for nexttask in self.sorted_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002120 if nexttask in self.sq_buildable and nexttask not in self.sq_running and self.sqdata.stamps[nexttask] not in self.build_stamps.values():
Andrew Geissler595f6302022-01-24 19:11:47 +00002121 if nexttask not in self.sqdata.unskippable and self.sqdata.sq_revdeps[nexttask] and self.sqdata.sq_revdeps[nexttask].issubset(self.scenequeue_covered) and self.check_dependencies(nexttask, self.sqdata.sq_revdeps[nexttask]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002122 if nexttask not in self.rqdata.target_tids:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002123 logger.debug2("Skipping setscene for task %s" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002124 self.sq_task_skip(nexttask)
2125 self.scenequeue_notneeded.add(nexttask)
2126 if nexttask in self.sq_deferred:
2127 del self.sq_deferred[nexttask]
2128 return True
Brad Bishop08902b02019-08-20 09:16:51 -04002129 # If covered tasks are running, need to wait for them to complete
2130 for t in self.sqdata.sq_covered_tasks[nexttask]:
2131 if t in self.runq_running and t not in self.runq_complete:
2132 continue
Brad Bishop96ff1982019-08-19 13:50:42 -04002133 if nexttask in self.sq_deferred:
2134 if self.sq_deferred[nexttask] not in self.runq_complete:
2135 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002136 logger.debug("Task %s no longer deferred" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002137 del self.sq_deferred[nexttask]
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002138 valid = self.rq.validate_hashes(set([nexttask]), self.cooker.data, 0, False, summary=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002139 if not valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002140 logger.debug("%s didn't become valid, skipping setscene" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002141 self.sq_task_failoutright(nexttask)
2142 return True
Brad Bishop96ff1982019-08-19 13:50:42 -04002143 if nexttask in self.sqdata.outrightfail:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002144 logger.debug2('No package found, so skipping setscene task %s', nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002145 self.sq_task_failoutright(nexttask)
2146 return True
2147 if nexttask in self.sqdata.unskippable:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002148 logger.debug2("Setscene task %s is unskippable" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002149 task = nexttask
2150 break
2151 if task is not None:
2152 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
2153 taskname = taskname + "_setscene"
2154 if self.rq.check_stamp_task(task, taskname_from_tid(task), recurse = True, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002155 logger.debug2('Stamp for underlying task %s is current, so skipping setscene variant', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002156 self.sq_task_failoutright(task)
2157 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002158
Brad Bishop96ff1982019-08-19 13:50:42 -04002159 if self.cooker.configuration.force:
2160 if task in self.rqdata.target_tids:
2161 self.sq_task_failoutright(task)
2162 return True
2163
2164 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002165 logger.debug2('Setscene stamp current task %s, so skip it and its dependencies', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002166 self.sq_task_skip(task)
2167 return True
2168
2169 if self.cooker.configuration.skipsetscene:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002170 logger.debug2('No setscene tasks should be executed. Skipping %s', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002171 self.sq_task_failoutright(task)
2172 return True
2173
Andrew Geissler5199d832021-09-24 16:47:35 -05002174 startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
Brad Bishop96ff1982019-08-19 13:50:42 -04002175 bb.event.fire(startevent, self.cfgData)
2176
Brad Bishop96ff1982019-08-19 13:50:42 -04002177 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002178 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002179 runtask = {
2180 'fn' : taskfn,
2181 'task' : task,
2182 'taskname' : taskname,
2183 'taskhash' : self.rqdata.get_task_hash(task),
2184 'unihash' : self.rqdata.get_task_unihash(task),
2185 'quieterrors' : True,
2186 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002187 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002188 'taskdepdata' : self.sq_build_taskdepdata(task),
2189 'dry_run' : False,
2190 'taskdep': taskdep,
2191 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2192 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2193 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2194 }
2195
Brad Bishop96ff1982019-08-19 13:50:42 -04002196 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
2197 if not mc in self.rq.fakeworker:
2198 self.rq.start_fakeworker(self, mc)
Andrew Geissler517393d2023-01-13 08:55:19 -06002199 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002200 self.rq.fakeworker[mc].process.stdin.flush()
2201 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06002202 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002203 self.rq.worker[mc].process.stdin.flush()
2204
Andrew Geissler517393d2023-01-13 08:55:19 -06002205 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002206 self.build_stamps2.append(self.build_stamps[task])
2207 self.sq_running.add(task)
2208 self.sq_live.add(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05002209 self.stats.updateActiveSetscene(len(self.sq_live))
Brad Bishop96ff1982019-08-19 13:50:42 -04002210 if self.can_start_task():
2211 return True
2212
Brad Bishopc68388fc2019-08-26 01:33:31 -04002213 self.update_holdofftasks()
2214
Brad Bishop08902b02019-08-20 09:16:51 -04002215 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 -05002216 hashequiv_logger.verbose("Setscene tasks completed")
Brad Bishop96ff1982019-08-19 13:50:42 -04002217
Brad Bishop08902b02019-08-20 09:16:51 -04002218 err = self.summarise_scenequeue_errors()
Brad Bishop96ff1982019-08-19 13:50:42 -04002219 if err:
2220 self.rq.state = runQueueFailed
2221 return True
2222
2223 if self.cooker.configuration.setsceneonly:
2224 self.rq.state = runQueueComplete
2225 return True
2226 self.sqdone = True
2227
2228 if self.stats.total == 0:
2229 # nothing to do
2230 self.rq.state = runQueueComplete
2231 return True
2232
2233 if self.cooker.configuration.setsceneonly:
2234 task = None
2235 else:
2236 task = self.sched.next()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002237 if task is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002238 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002239
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002240 if self.rqdata.setscene_ignore_tasks is not None:
2241 if self.check_setscene_ignore_tasks(task):
2242 self.task_fail(task, "setscene ignore_tasks")
Brad Bishop96ff1982019-08-19 13:50:42 -04002243 return True
2244
2245 if task in self.tasks_covered:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002246 logger.debug2("Setscene covered task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002247 self.task_skip(task, "covered")
2248 return True
2249
2250 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002251 logger.debug2("Stamp current task %s", task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002252
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002253 self.task_skip(task, "existing")
Andrew Geissler82c905d2020-04-13 13:39:40 -05002254 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002255 return True
2256
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002257 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002258 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2259 startevent = runQueueTaskStarted(task, self.stats, self.rq,
2260 noexec=True)
2261 bb.event.fire(startevent, self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002262 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002263 self.stats.taskActive()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002264 if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Andrew Geissler517393d2023-01-13 08:55:19 -06002265 bb.build.make_stamp_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002266 self.task_complete(task)
2267 return True
2268 else:
2269 startevent = runQueueTaskStarted(task, self.stats, self.rq)
2270 bb.event.fire(startevent, self.cfgData)
2271
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002272 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002273 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002274 runtask = {
2275 'fn' : taskfn,
2276 'task' : task,
2277 'taskname' : taskname,
2278 'taskhash' : self.rqdata.get_task_hash(task),
2279 'unihash' : self.rqdata.get_task_unihash(task),
2280 'quieterrors' : False,
2281 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002282 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002283 'taskdepdata' : self.build_taskdepdata(task),
2284 'dry_run' : self.rqdata.setscene_enforce,
2285 'taskdep': taskdep,
2286 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2287 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2288 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2289 }
2290
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002291 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 -05002292 if not mc in self.rq.fakeworker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002293 try:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002294 self.rq.start_fakeworker(self, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002295 except OSError as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002296 logger.critical("Failed to spawn fakeroot worker to run %s: %s" % (task, str(exc)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002297 self.rq.state = runQueueFailed
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002298 self.stats.taskFailed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002299 return True
Andrew Geissler517393d2023-01-13 08:55:19 -06002300 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002301 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002302 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06002303 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps(runtask) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002304 self.rq.worker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002305
Andrew Geissler517393d2023-01-13 08:55:19 -06002306 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002307 self.build_stamps2.append(self.build_stamps[task])
2308 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002309 self.stats.taskActive()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002310 if self.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002311 return True
2312
Andrew Geissler595f6302022-01-24 19:11:47 +00002313 if self.stats.active > 0 or self.sq_live:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002314 self.rq.read_workers()
2315 return self.rq.active_fds()
2316
Brad Bishop96ff1982019-08-19 13:50:42 -04002317 # No more tasks can be run. If we have deferred setscene tasks we should run them.
2318 if self.sq_deferred:
Patrick Williams92b42cb2022-09-03 06:53:57 -05002319 deferred_tid = list(self.sq_deferred.keys())[0]
2320 blocking_tid = self.sq_deferred.pop(deferred_tid)
Patrick Williams2390b1b2022-11-03 13:47:49 -05002321 logger.warning("Runqueue deadlocked on deferred tasks, forcing task %s blocked by %s" % (deferred_tid, blocking_tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04002322 return True
2323
Andrew Geissler595f6302022-01-24 19:11:47 +00002324 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002325 self.rq.state = runQueueFailed
2326 return True
2327
2328 # Sanity Checks
Brad Bishop08902b02019-08-20 09:16:51 -04002329 err = self.summarise_scenequeue_errors()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002330 for task in self.rqdata.runtaskentries:
2331 if task not in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002332 logger.error("Task %s never buildable!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002333 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002334 elif task not in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002335 logger.error("Task %s never ran!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002336 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002337 elif task not in self.runq_complete:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002338 logger.error("Task %s never completed!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002339 err = True
2340
2341 if err:
2342 self.rq.state = runQueueFailed
2343 else:
2344 self.rq.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002345
2346 return True
2347
Brad Bishopc68388fc2019-08-26 01:33:31 -04002348 def filtermcdeps(self, task, mc, deps):
Andrew Geissler99467da2019-02-25 18:54:23 -06002349 ret = set()
Andrew Geissler99467da2019-02-25 18:54:23 -06002350 for dep in deps:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002351 thismc = mc_from_tid(dep)
2352 if thismc != mc:
Andrew Geissler99467da2019-02-25 18:54:23 -06002353 continue
2354 ret.add(dep)
2355 return ret
2356
Brad Bishopa34c0302019-09-23 22:34:48 -04002357 # We filter out multiconfig dependencies from taskdepdata we pass to the tasks
Andrew Geissler99467da2019-02-25 18:54:23 -06002358 # as most code can't handle them
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002359 def build_taskdepdata(self, task):
2360 taskdepdata = {}
Brad Bishopc68388fc2019-08-26 01:33:31 -04002361 mc = mc_from_tid(task)
Brad Bishop08902b02019-08-20 09:16:51 -04002362 next = self.rqdata.runtaskentries[task].depends.copy()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002363 next.add(task)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002364 next = self.filtermcdeps(task, mc, next)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002365 while next:
2366 additional = []
2367 for revdep in next:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002368 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2369 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2370 deps = self.rqdata.runtaskentries[revdep].depends
2371 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002372 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002373 unihash = self.rqdata.runtaskentries[revdep].unihash
Brad Bishopc68388fc2019-08-26 01:33:31 -04002374 deps = self.filtermcdeps(task, mc, deps)
Patrick Williamsb542dec2023-06-09 01:26:37 -05002375 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2376 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002377 for revdep2 in deps:
2378 if revdep2 not in taskdepdata:
2379 additional.append(revdep2)
2380 next = additional
2381
2382 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2383 return taskdepdata
2384
Brad Bishop08902b02019-08-20 09:16:51 -04002385 def update_holdofftasks(self):
Brad Bishopc68388fc2019-08-26 01:33:31 -04002386
2387 if not self.holdoff_need_update:
2388 return
2389
2390 notcovered = set(self.scenequeue_notcovered)
2391 notcovered |= self.cantskip
2392 for tid in self.scenequeue_notcovered:
2393 notcovered |= self.sqdata.sq_covered_tasks[tid]
2394 notcovered |= self.sqdata.unskippable.difference(self.rqdata.runq_setscene_tids)
2395 notcovered.intersection_update(self.tasks_scenequeue_done)
2396
2397 covered = set(self.scenequeue_covered)
2398 for tid in self.scenequeue_covered:
2399 covered |= self.sqdata.sq_covered_tasks[tid]
2400 covered.difference_update(notcovered)
2401 covered.intersection_update(self.tasks_scenequeue_done)
2402
2403 for tid in notcovered | covered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002404 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002405 self.setbuildable(tid)
2406 elif self.rqdata.runtaskentries[tid].depends.issubset(self.runq_complete):
2407 self.setbuildable(tid)
2408
2409 self.tasks_covered = covered
2410 self.tasks_notcovered = notcovered
2411
Brad Bishop08902b02019-08-20 09:16:51 -04002412 self.holdoff_tasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002413
Brad Bishop08902b02019-08-20 09:16:51 -04002414 for tid in self.rqdata.runq_setscene_tids:
2415 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2416 self.holdoff_tasks.add(tid)
2417
2418 for tid in self.holdoff_tasks.copy():
2419 for dep in self.sqdata.sq_covered_tasks[tid]:
2420 if dep not in self.runq_complete:
2421 self.holdoff_tasks.add(dep)
2422
Brad Bishopc68388fc2019-08-26 01:33:31 -04002423 self.holdoff_need_update = False
2424
Brad Bishop08902b02019-08-20 09:16:51 -04002425 def process_possible_migrations(self):
2426
2427 changed = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002428 toprocess = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002429 for tid, unihash in self.updated_taskhash_queue.copy():
2430 if tid in self.runq_running and tid not in self.runq_complete:
2431 continue
2432
2433 self.updated_taskhash_queue.remove((tid, unihash))
2434
2435 if unihash != self.rqdata.runtaskentries[tid].unihash:
Andrew Geisslerc926e172021-05-07 16:11:35 -05002436 # Make sure we rehash any other tasks with the same task hash that we're deferred against.
2437 torehash = [tid]
2438 for deftid in self.sq_deferred:
2439 if self.sq_deferred[deftid] == tid:
2440 torehash.append(deftid)
2441 for hashtid in torehash:
2442 hashequiv_logger.verbose("Task %s unihash changed to %s" % (hashtid, unihash))
2443 self.rqdata.runtaskentries[hashtid].unihash = unihash
2444 bb.parse.siggen.set_unihash(hashtid, unihash)
2445 toprocess.add(hashtid)
Andrew Geissler78b72792022-06-14 06:47:25 -05002446 if torehash:
2447 # Need to save after set_unihash above
2448 bb.parse.siggen.save_unitaskhashes()
Brad Bishop08902b02019-08-20 09:16:51 -04002449
Andrew Geissler82c905d2020-04-13 13:39:40 -05002450 # Work out all tasks which depend upon these
2451 total = set()
2452 next = set()
2453 for p in toprocess:
2454 next |= self.rqdata.runtaskentries[p].revdeps
2455 while next:
2456 current = next.copy()
2457 total = total | next
2458 next = set()
2459 for ntid in current:
2460 next |= self.rqdata.runtaskentries[ntid].revdeps
2461 next.difference_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002462
Andrew Geissler82c905d2020-04-13 13:39:40 -05002463 # Now iterate those tasks in dependency order to regenerate their taskhash/unihash
2464 next = set()
2465 for p in total:
Andrew Geissler595f6302022-01-24 19:11:47 +00002466 if not self.rqdata.runtaskentries[p].depends:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002467 next.add(p)
2468 elif self.rqdata.runtaskentries[p].depends.isdisjoint(total):
2469 next.add(p)
2470
2471 # When an item doesn't have dependencies in total, we can process it. Drop items from total when handled
2472 while next:
2473 current = next.copy()
2474 next = set()
2475 for tid in current:
Andrew Geissler595f6302022-01-24 19:11:47 +00002476 if self.rqdata.runtaskentries[p].depends and not self.rqdata.runtaskentries[tid].depends.isdisjoint(total):
Andrew Geissler82c905d2020-04-13 13:39:40 -05002477 continue
2478 orighash = self.rqdata.runtaskentries[tid].hash
Andrew Geissler517393d2023-01-13 08:55:19 -06002479 newhash = bb.parse.siggen.get_taskhash(tid, self.rqdata.runtaskentries[tid].depends, self.rqdata.dataCaches)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002480 origuni = self.rqdata.runtaskentries[tid].unihash
2481 newuni = bb.parse.siggen.get_unihash(tid)
2482 # FIXME, need to check it can come from sstate at all for determinism?
2483 remapped = False
2484 if newuni == origuni:
2485 # Nothing to do, we match, skip code below
2486 remapped = True
2487 elif tid in self.scenequeue_covered or tid in self.sq_live:
2488 # Already ran this setscene task or it running. Report the new taskhash
2489 bb.parse.siggen.report_unihash_equiv(tid, newhash, origuni, newuni, self.rqdata.dataCaches)
2490 hashequiv_logger.verbose("Already covered setscene for %s so ignoring rehash (remap)" % (tid))
2491 remapped = True
2492
2493 if not remapped:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002494 #logger.debug("Task %s hash changes: %s->%s %s->%s" % (tid, orighash, newhash, origuni, newuni))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002495 self.rqdata.runtaskentries[tid].hash = newhash
2496 self.rqdata.runtaskentries[tid].unihash = newuni
2497 changed.add(tid)
2498
2499 next |= self.rqdata.runtaskentries[tid].revdeps
2500 total.remove(tid)
2501 next.intersection_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002502
2503 if changed:
2504 for mc in self.rq.worker:
2505 self.rq.worker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2506 for mc in self.rq.fakeworker:
2507 self.rq.fakeworker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2508
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002509 hashequiv_logger.debug(pprint.pformat("Tasks changed:\n%s" % (changed)))
Brad Bishop08902b02019-08-20 09:16:51 -04002510
2511 for tid in changed:
2512 if tid not in self.rqdata.runq_setscene_tids:
2513 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002514 if tid not in self.pending_migrations:
2515 self.pending_migrations.add(tid)
2516
Andrew Geissler82c905d2020-04-13 13:39:40 -05002517 update_tasks = []
Brad Bishop08902b02019-08-20 09:16:51 -04002518 for tid in self.pending_migrations.copy():
Andrew Geissler82c905d2020-04-13 13:39:40 -05002519 if tid in self.runq_running or tid in self.sq_live:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002520 # Too late, task already running, not much we can do now
2521 self.pending_migrations.remove(tid)
2522 continue
2523
Brad Bishop08902b02019-08-20 09:16:51 -04002524 valid = True
2525 # Check no tasks this covers are running
2526 for dep in self.sqdata.sq_covered_tasks[tid]:
2527 if dep in self.runq_running and dep not in self.runq_complete:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002528 hashequiv_logger.debug2("Task %s is running which blocks setscene for %s from running" % (dep, tid))
Brad Bishop08902b02019-08-20 09:16:51 -04002529 valid = False
2530 break
2531 if not valid:
2532 continue
2533
2534 self.pending_migrations.remove(tid)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002535 changed = True
Brad Bishop08902b02019-08-20 09:16:51 -04002536
2537 if tid in self.tasks_scenequeue_done:
2538 self.tasks_scenequeue_done.remove(tid)
2539 for dep in self.sqdata.sq_covered_tasks[tid]:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002540 if dep in self.runq_complete and dep not in self.runq_tasksrun:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002541 bb.error("Task %s marked as completed but now needing to rerun? Halting build." % dep)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002542 self.failed_tids.append(tid)
2543 self.rq.state = runQueueCleanUp
2544 return
2545
Brad Bishop08902b02019-08-20 09:16:51 -04002546 if dep not in self.runq_complete:
2547 if dep in self.tasks_scenequeue_done and dep not in self.sqdata.unskippable:
2548 self.tasks_scenequeue_done.remove(dep)
2549
2550 if tid in self.sq_buildable:
2551 self.sq_buildable.remove(tid)
2552 if tid in self.sq_running:
2553 self.sq_running.remove(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002554 if tid in self.sqdata.outrightfail:
2555 self.sqdata.outrightfail.remove(tid)
2556 if tid in self.scenequeue_notcovered:
2557 self.scenequeue_notcovered.remove(tid)
2558 if tid in self.scenequeue_covered:
2559 self.scenequeue_covered.remove(tid)
2560 if tid in self.scenequeue_notneeded:
2561 self.scenequeue_notneeded.remove(tid)
2562
2563 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2564 self.sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2565
2566 if tid in self.stampcache:
2567 del self.stampcache[tid]
2568
2569 if tid in self.build_stamps:
2570 del self.build_stamps[tid]
2571
2572 update_tasks.append(tid)
2573
2574 update_tasks2 = []
2575 for tid in update_tasks:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002576 harddepfail = False
2577 for t in self.sqdata.sq_harddeps:
2578 if tid in self.sqdata.sq_harddeps[t] and t in self.scenequeue_notcovered:
2579 harddepfail = True
2580 break
2581 if not harddepfail and self.sqdata.sq_revdeps[tid].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
Brad Bishop08902b02019-08-20 09:16:51 -04002582 if tid not in self.sq_buildable:
2583 self.sq_buildable.add(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +00002584 if not self.sqdata.sq_revdeps[tid]:
Brad Bishop08902b02019-08-20 09:16:51 -04002585 self.sq_buildable.add(tid)
2586
Andrew Geissler517393d2023-01-13 08:55:19 -06002587 update_tasks2.append((tid, harddepfail, tid in self.sqdata.valid))
Brad Bishop08902b02019-08-20 09:16:51 -04002588
Andrew Geissler517393d2023-01-13 08:55:19 -06002589 if update_tasks2:
Brad Bishop08902b02019-08-20 09:16:51 -04002590 self.sqdone = False
Patrick Williams92b42cb2022-09-03 06:53:57 -05002591 for mc in sorted(self.sqdata.multiconfigs):
Andrew Geissler517393d2023-01-13 08:55:19 -06002592 for tid in sorted([t[0] for t in update_tasks2]):
Patrick Williams92b42cb2022-09-03 06:53:57 -05002593 if mc_from_tid(tid) != mc:
2594 continue
2595 h = pending_hash_index(tid, self.rqdata)
2596 if h in self.sqdata.hashes and tid != self.sqdata.hashes[h]:
2597 self.sq_deferred[tid] = self.sqdata.hashes[h]
2598 bb.note("Deferring %s after %s" % (tid, self.sqdata.hashes[h]))
Andrew Geissler517393d2023-01-13 08:55:19 -06002599 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 -05002600
Andrew Geissler517393d2023-01-13 08:55:19 -06002601 for (tid, harddepfail, origvalid) in update_tasks2:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002602 if tid in self.sqdata.valid and not origvalid:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002603 hashequiv_logger.verbose("Setscene task %s became valid" % tid)
2604 if harddepfail:
Andrew Geissler517393d2023-01-13 08:55:19 -06002605 logger.debug2("%s has an unavailable hard dependency so skipping" % (tid))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002606 self.sq_task_failoutright(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002607
2608 if changed:
Andrew Geissler5199d832021-09-24 16:47:35 -05002609 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002610 self.holdoff_need_update = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002611
Brad Bishop96ff1982019-08-19 13:50:42 -04002612 def scenequeue_updatecounters(self, task, fail=False):
Brad Bishop08902b02019-08-20 09:16:51 -04002613
2614 for dep in sorted(self.sqdata.sq_deps[task]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002615 if fail and task in self.sqdata.sq_harddeps and dep in self.sqdata.sq_harddeps[task]:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002616 if dep in self.scenequeue_covered or dep in self.scenequeue_notcovered:
2617 # dependency could be already processed, e.g. noexec setscene task
2618 continue
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002619 noexec, stamppresent = check_setscene_stamps(dep, self.rqdata, self.rq, self.stampcache)
2620 if noexec or stamppresent:
2621 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002622 logger.debug2("%s was unavailable and is a hard dependency of %s so skipping" % (task, dep))
Brad Bishop96ff1982019-08-19 13:50:42 -04002623 self.sq_task_failoutright(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002624 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002625 if self.sqdata.sq_revdeps[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2626 if dep not in self.sq_buildable:
2627 self.sq_buildable.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002628
Brad Bishop96ff1982019-08-19 13:50:42 -04002629 next = set([task])
2630 while next:
2631 new = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002632 for t in sorted(next):
Brad Bishop96ff1982019-08-19 13:50:42 -04002633 self.tasks_scenequeue_done.add(t)
2634 # Look down the dependency chain for non-setscene things which this task depends on
2635 # and mark as 'done'
2636 for dep in self.rqdata.runtaskentries[t].depends:
2637 if dep in self.rqdata.runq_setscene_tids or dep in self.tasks_scenequeue_done:
2638 continue
2639 if self.rqdata.runtaskentries[dep].revdeps.issubset(self.tasks_scenequeue_done):
2640 new.add(dep)
Brad Bishop96ff1982019-08-19 13:50:42 -04002641 next = new
2642
Andrew Geissler5199d832021-09-24 16:47:35 -05002643 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002644 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002645
2646 def sq_task_completeoutright(self, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002647 """
2648 Mark a task as completed
2649 Look at the reverse dependencies and mark any task with
2650 completed dependencies as buildable
2651 """
2652
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002653 logger.debug('Found task %s which could be accelerated', task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002654 self.scenequeue_covered.add(task)
2655 self.scenequeue_updatecounters(task)
2656
Brad Bishop96ff1982019-08-19 13:50:42 -04002657 def sq_check_taskfail(self, task):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002658 if self.rqdata.setscene_ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002659 realtask = task.split('_setscene')[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002660 (mc, fn, taskname, taskfn) = split_tid_mcfn(realtask)
2661 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002662 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002663 logger.error('Task %s.%s failed' % (pn, taskname + "_setscene"))
2664 self.rq.state = runQueueCleanUp
2665
Brad Bishop96ff1982019-08-19 13:50:42 -04002666 def sq_task_complete(self, task):
Andrew Geissler5199d832021-09-24 16:47:35 -05002667 bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
Brad Bishop96ff1982019-08-19 13:50:42 -04002668 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002669
Brad Bishop96ff1982019-08-19 13:50:42 -04002670 def sq_task_fail(self, task, result):
Andrew Geissler5199d832021-09-24 16:47:35 -05002671 bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002672 self.scenequeue_notcovered.add(task)
2673 self.scenequeue_updatecounters(task, True)
Brad Bishop96ff1982019-08-19 13:50:42 -04002674 self.sq_check_taskfail(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002675
Brad Bishop96ff1982019-08-19 13:50:42 -04002676 def sq_task_failoutright(self, task):
2677 self.sq_running.add(task)
2678 self.sq_buildable.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002679 self.scenequeue_notcovered.add(task)
2680 self.scenequeue_updatecounters(task, True)
2681
Brad Bishop96ff1982019-08-19 13:50:42 -04002682 def sq_task_skip(self, task):
2683 self.sq_running.add(task)
2684 self.sq_buildable.add(task)
2685 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002686
Brad Bishop96ff1982019-08-19 13:50:42 -04002687 def sq_build_taskdepdata(self, task):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002688 def getsetscenedeps(tid):
2689 deps = set()
2690 (mc, fn, taskname, _) = split_tid_mcfn(tid)
2691 realtid = tid + "_setscene"
2692 idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
2693 for (depname, idependtask) in idepends:
2694 if depname not in self.rqdata.taskData[mc].build_targets:
2695 continue
2696
2697 depfn = self.rqdata.taskData[mc].build_targets[depname][0]
2698 if depfn is None:
2699 continue
2700 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2701 deps.add(deptid)
2702 return deps
2703
2704 taskdepdata = {}
2705 next = getsetscenedeps(task)
2706 next.add(task)
2707 while next:
2708 additional = []
2709 for revdep in next:
2710 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2711 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2712 deps = getsetscenedeps(revdep)
2713 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2714 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002715 unihash = self.rqdata.runtaskentries[revdep].unihash
Patrick Williamsb542dec2023-06-09 01:26:37 -05002716 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2717 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002718 for revdep2 in deps:
2719 if revdep2 not in taskdepdata:
2720 additional.append(revdep2)
2721 next = additional
2722
2723 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2724 return taskdepdata
2725
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002726 def check_setscene_ignore_tasks(self, tid):
2727 # Check task that is going to run against the ignore tasks list
Brad Bishop96ff1982019-08-19 13:50:42 -04002728 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2729 # Ignore covered tasks
2730 if tid in self.tasks_covered:
2731 return False
2732 # Ignore stamped tasks
2733 if self.rq.check_stamp_task(tid, taskname, cache=self.stampcache):
2734 return False
2735 # Ignore noexec tasks
2736 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2737 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2738 return False
2739
2740 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002741 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Brad Bishop96ff1982019-08-19 13:50:42 -04002742 if tid in self.rqdata.runq_setscene_tids:
Andrew Geissler595f6302022-01-24 19:11:47 +00002743 msg = ['Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)]
Brad Bishop96ff1982019-08-19 13:50:42 -04002744 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002745 msg = ['Task %s.%s attempted to execute unexpectedly' % (pn, taskname)]
Andrew Geissler82c905d2020-04-13 13:39:40 -05002746 for t in self.scenequeue_notcovered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002747 msg.append("\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash))
2748 msg.append('\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered))
2749 logger.error("".join(msg))
Brad Bishop96ff1982019-08-19 13:50:42 -04002750 return True
2751 return False
2752
2753class SQData(object):
2754 def __init__(self):
2755 # SceneQueue dependencies
2756 self.sq_deps = {}
2757 # SceneQueue reverse dependencies
2758 self.sq_revdeps = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002759 # Injected inter-setscene task dependencies
2760 self.sq_harddeps = {}
2761 # Cache of stamp files so duplicates can't run in parallel
2762 self.stamps = {}
2763 # Setscene tasks directly depended upon by the build
2764 self.unskippable = set()
2765 # List of setscene tasks which aren't present
2766 self.outrightfail = set()
2767 # A list of normal tasks a setscene task covers
2768 self.sq_covered_tasks = {}
2769
2770def build_scenequeue_data(sqdata, rqdata, rq, cooker, stampcache, sqrq):
2771
2772 sq_revdeps = {}
2773 sq_revdeps_squash = {}
2774 sq_collated_deps = {}
2775
2776 # We need to construct a dependency graph for the setscene functions. Intermediate
2777 # dependencies between the setscene tasks only complicate the code. This code
2778 # therefore aims to collapse the huge runqueue dependency tree into a smaller one
2779 # only containing the setscene functions.
2780
2781 rqdata.init_progress_reporter.next_stage()
2782
2783 # First process the chains up to the first setscene task.
2784 endpoints = {}
2785 for tid in rqdata.runtaskentries:
2786 sq_revdeps[tid] = copy.copy(rqdata.runtaskentries[tid].revdeps)
2787 sq_revdeps_squash[tid] = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00002788 if not sq_revdeps[tid] and tid not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002789 #bb.warn("Added endpoint %s" % (tid))
2790 endpoints[tid] = set()
2791
2792 rqdata.init_progress_reporter.next_stage()
2793
2794 # Secondly process the chains between setscene tasks.
2795 for tid in rqdata.runq_setscene_tids:
2796 sq_collated_deps[tid] = set()
2797 #bb.warn("Added endpoint 2 %s" % (tid))
2798 for dep in rqdata.runtaskentries[tid].depends:
2799 if tid in sq_revdeps[dep]:
2800 sq_revdeps[dep].remove(tid)
2801 if dep not in endpoints:
2802 endpoints[dep] = set()
2803 #bb.warn(" Added endpoint 3 %s" % (dep))
2804 endpoints[dep].add(tid)
2805
2806 rqdata.init_progress_reporter.next_stage()
2807
2808 def process_endpoints(endpoints):
2809 newendpoints = {}
2810 for point, task in endpoints.items():
2811 tasks = set()
2812 if task:
2813 tasks |= task
2814 if sq_revdeps_squash[point]:
2815 tasks |= sq_revdeps_squash[point]
2816 if point not in rqdata.runq_setscene_tids:
2817 for t in tasks:
2818 sq_collated_deps[t].add(point)
2819 sq_revdeps_squash[point] = set()
2820 if point in rqdata.runq_setscene_tids:
2821 sq_revdeps_squash[point] = tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002822 continue
2823 for dep in rqdata.runtaskentries[point].depends:
2824 if point in sq_revdeps[dep]:
2825 sq_revdeps[dep].remove(point)
2826 if tasks:
2827 sq_revdeps_squash[dep] |= tasks
Andrew Geissler595f6302022-01-24 19:11:47 +00002828 if not sq_revdeps[dep] and dep not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002829 newendpoints[dep] = task
Andrew Geissler595f6302022-01-24 19:11:47 +00002830 if newendpoints:
Brad Bishop96ff1982019-08-19 13:50:42 -04002831 process_endpoints(newendpoints)
2832
2833 process_endpoints(endpoints)
2834
2835 rqdata.init_progress_reporter.next_stage()
2836
Brad Bishop08902b02019-08-20 09:16:51 -04002837 # Build a list of tasks which are "unskippable"
2838 # These are direct endpoints referenced by the build upto and including setscene tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002839 # Take the build endpoints (no revdeps) and find the sstate tasks they depend upon
2840 new = True
2841 for tid in rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00002842 if not rqdata.runtaskentries[tid].revdeps:
Brad Bishop96ff1982019-08-19 13:50:42 -04002843 sqdata.unskippable.add(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002844 sqdata.unskippable |= sqrq.cantskip
Brad Bishop96ff1982019-08-19 13:50:42 -04002845 while new:
2846 new = False
Brad Bishop08902b02019-08-20 09:16:51 -04002847 orig = sqdata.unskippable.copy()
2848 for tid in sorted(orig, reverse=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04002849 if tid in rqdata.runq_setscene_tids:
2850 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00002851 if not rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04002852 # These are tasks which have no setscene tasks in their chain, need to mark as directly buildable
Brad Bishop96ff1982019-08-19 13:50:42 -04002853 sqrq.setbuildable(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002854 sqdata.unskippable |= rqdata.runtaskentries[tid].depends
Brad Bishop08902b02019-08-20 09:16:51 -04002855 if sqdata.unskippable != orig:
2856 new = True
2857
2858 sqrq.tasks_scenequeue_done |= sqdata.unskippable.difference(rqdata.runq_setscene_tids)
Brad Bishop96ff1982019-08-19 13:50:42 -04002859
2860 rqdata.init_progress_reporter.next_stage(len(rqdata.runtaskentries))
2861
2862 # Sanity check all dependencies could be changed to setscene task references
2863 for taskcounter, tid in enumerate(rqdata.runtaskentries):
2864 if tid in rqdata.runq_setscene_tids:
2865 pass
Andrew Geissler595f6302022-01-24 19:11:47 +00002866 elif sq_revdeps_squash[tid]:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002867 bb.msg.fatal("RunQueue", "Something went badly wrong during scenequeue generation, halting. Please report this problem.")
Brad Bishop96ff1982019-08-19 13:50:42 -04002868 else:
2869 del sq_revdeps_squash[tid]
2870 rqdata.init_progress_reporter.update(taskcounter)
2871
2872 rqdata.init_progress_reporter.next_stage()
2873
2874 # Resolve setscene inter-task dependencies
2875 # e.g. do_sometask_setscene[depends] = "targetname:do_someothertask_setscene"
2876 # Note that anything explicitly depended upon will have its reverse dependencies removed to avoid circular dependencies
2877 for tid in rqdata.runq_setscene_tids:
2878 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2879 realtid = tid + "_setscene"
2880 idepends = rqdata.taskData[mc].taskentries[realtid].idepends
Andrew Geissler517393d2023-01-13 08:55:19 -06002881 sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2882
Brad Bishop96ff1982019-08-19 13:50:42 -04002883 for (depname, idependtask) in idepends:
2884
2885 if depname not in rqdata.taskData[mc].build_targets:
2886 continue
2887
2888 depfn = rqdata.taskData[mc].build_targets[depname][0]
2889 if depfn is None:
2890 continue
2891 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2892 if deptid not in rqdata.runtaskentries:
2893 bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (realtid, depfn, idependtask))
2894
2895 if not deptid in sqdata.sq_harddeps:
2896 sqdata.sq_harddeps[deptid] = set()
2897 sqdata.sq_harddeps[deptid].add(tid)
2898
2899 sq_revdeps_squash[tid].add(deptid)
2900 # Have to zero this to avoid circular dependencies
2901 sq_revdeps_squash[deptid] = set()
2902
2903 rqdata.init_progress_reporter.next_stage()
2904
2905 for task in sqdata.sq_harddeps:
2906 for dep in sqdata.sq_harddeps[task]:
2907 sq_revdeps_squash[dep].add(task)
2908
2909 rqdata.init_progress_reporter.next_stage()
2910
2911 #for tid in sq_revdeps_squash:
2912 # data = ""
2913 # for dep in sq_revdeps_squash[tid]:
2914 # data = data + "\n %s" % dep
2915 # bb.warn("Task %s_setscene: is %s " % (tid, data))
2916
2917 sqdata.sq_revdeps = sq_revdeps_squash
Brad Bishop96ff1982019-08-19 13:50:42 -04002918 sqdata.sq_covered_tasks = sq_collated_deps
2919
2920 # Build reverse version of revdeps to populate deps structure
2921 for tid in sqdata.sq_revdeps:
2922 sqdata.sq_deps[tid] = set()
2923 for tid in sqdata.sq_revdeps:
2924 for dep in sqdata.sq_revdeps[tid]:
2925 sqdata.sq_deps[dep].add(tid)
2926
2927 rqdata.init_progress_reporter.next_stage()
2928
Brad Bishop00e122a2019-10-05 11:10:57 -04002929 sqdata.multiconfigs = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002930 for tid in sqdata.sq_revdeps:
Brad Bishop00e122a2019-10-05 11:10:57 -04002931 sqdata.multiconfigs.add(mc_from_tid(tid))
Andrew Geissler595f6302022-01-24 19:11:47 +00002932 if not sqdata.sq_revdeps[tid]:
Brad Bishop96ff1982019-08-19 13:50:42 -04002933 sqrq.sq_buildable.add(tid)
2934
2935 rqdata.init_progress_reporter.finish()
2936
Brad Bishop00e122a2019-10-05 11:10:57 -04002937 sqdata.noexec = set()
2938 sqdata.stamppresent = set()
2939 sqdata.valid = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002940
Patrick Williams213cb262021-08-07 19:21:33 -05002941 sqdata.hashes = {}
2942 sqrq.sq_deferred = {}
2943 for mc in sorted(sqdata.multiconfigs):
2944 for tid in sorted(sqdata.sq_revdeps):
2945 if mc_from_tid(tid) != mc:
2946 continue
2947 h = pending_hash_index(tid, rqdata)
2948 if h not in sqdata.hashes:
2949 sqdata.hashes[h] = tid
2950 else:
2951 sqrq.sq_deferred[tid] = sqdata.hashes[h]
Andrew Geissler8f840682023-07-21 09:09:43 -05002952 bb.debug(1, "Deferring %s after %s" % (tid, sqdata.hashes[h]))
Patrick Williams213cb262021-08-07 19:21:33 -05002953
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002954 update_scenequeue_data(sqdata.sq_revdeps, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002955
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002956 # Compute a list of 'stale' sstate tasks where the current hash does not match the one
2957 # in any stamp files. Pass the list out to metadata as an event.
2958 found = {}
2959 for tid in rqdata.runq_setscene_tids:
2960 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002961 stamps = bb.build.find_stale_stamps(taskname, taskfn)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002962 if stamps:
2963 if mc not in found:
2964 found[mc] = {}
2965 found[mc][tid] = stamps
2966 for mc in found:
2967 event = bb.event.StaleSetSceneTasks(found[mc])
2968 bb.event.fire(event, cooker.databuilder.mcdata[mc])
2969
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002970def check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=False):
2971
2972 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2973
2974 taskdep = rqdata.dataCaches[mc].task_deps[taskfn]
2975
2976 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Andrew Geissler517393d2023-01-13 08:55:19 -06002977 bb.build.make_stamp_mcfn(taskname + "_setscene", taskfn)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002978 return True, False
2979
2980 if rq.check_stamp_task(tid, taskname + "_setscene", cache=stampcache):
2981 logger.debug2('Setscene stamp current for task %s', tid)
2982 return False, True
2983
2984 if rq.check_stamp_task(tid, taskname, recurse = True, cache=stampcache):
2985 logger.debug2('Normal stamp current for task %s', tid)
2986 return False, True
2987
2988 return False, False
2989
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002990def update_scenequeue_data(tids, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True):
Brad Bishop00e122a2019-10-05 11:10:57 -04002991
2992 tocheck = set()
2993
2994 for tid in sorted(tids):
2995 if tid in sqdata.stamppresent:
2996 sqdata.stamppresent.remove(tid)
2997 if tid in sqdata.valid:
2998 sqdata.valid.remove(tid)
Andrew Geisslerc926e172021-05-07 16:11:35 -05002999 if tid in sqdata.outrightfail:
3000 sqdata.outrightfail.remove(tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04003001
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003002 noexec, stamppresent = check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04003003
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003004 if noexec:
Brad Bishop00e122a2019-10-05 11:10:57 -04003005 sqdata.noexec.add(tid)
3006 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003007 logger.debug2("%s is noexec so skipping setscene" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04003008 continue
3009
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003010 if stamppresent:
Brad Bishop00e122a2019-10-05 11:10:57 -04003011 sqdata.stamppresent.add(tid)
3012 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003013 logger.debug2("%s has a valid stamp, skipping" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04003014 continue
3015
3016 tocheck.add(tid)
3017
Brad Bishop1d80a2e2019-11-15 16:35:03 -05003018 sqdata.valid |= rq.validate_hashes(tocheck, cooker.data, len(sqdata.stamppresent), False, summary=summary)
Brad Bishop00e122a2019-10-05 11:10:57 -04003019
Patrick Williams213cb262021-08-07 19:21:33 -05003020 for tid in tids:
3021 if tid in sqdata.stamppresent:
3022 continue
3023 if tid in sqdata.valid:
3024 continue
3025 if tid in sqdata.noexec:
3026 continue
3027 if tid in sqrq.scenequeue_covered:
3028 continue
3029 if tid in sqrq.scenequeue_notcovered:
3030 continue
3031 if tid in sqrq.sq_deferred:
3032 continue
3033 sqdata.outrightfail.add(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003034 logger.debug2("%s already handled (fallthrough), skipping" % (tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04003035
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003036class TaskFailure(Exception):
3037 """
3038 Exception raised when a task in a runqueue fails
3039 """
3040 def __init__(self, x):
3041 self.args = x
3042
3043
3044class runQueueExitWait(bb.event.Event):
3045 """
3046 Event when waiting for task processes to exit
3047 """
3048
3049 def __init__(self, remain):
3050 self.remain = remain
3051 self.message = "Waiting for %s active tasks to finish" % remain
3052 bb.event.Event.__init__(self)
3053
3054class runQueueEvent(bb.event.Event):
3055 """
3056 Base runQueue event class
3057 """
3058 def __init__(self, task, stats, rq):
3059 self.taskid = task
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003060 self.taskstring = task
3061 self.taskname = taskname_from_tid(task)
3062 self.taskfile = fn_from_tid(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003063 self.taskhash = rq.rqdata.get_task_hash(task)
3064 self.stats = stats.copy()
3065 bb.event.Event.__init__(self)
3066
3067class sceneQueueEvent(runQueueEvent):
3068 """
3069 Base sceneQueue event class
3070 """
3071 def __init__(self, task, stats, rq, noexec=False):
3072 runQueueEvent.__init__(self, task, stats, rq)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003073 self.taskstring = task + "_setscene"
3074 self.taskname = taskname_from_tid(task) + "_setscene"
3075 self.taskfile = fn_from_tid(task)
3076 self.taskhash = rq.rqdata.get_task_hash(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003077
3078class runQueueTaskStarted(runQueueEvent):
3079 """
3080 Event notifying a task was started
3081 """
3082 def __init__(self, task, stats, rq, noexec=False):
3083 runQueueEvent.__init__(self, task, stats, rq)
3084 self.noexec = noexec
3085
3086class sceneQueueTaskStarted(sceneQueueEvent):
3087 """
3088 Event notifying a setscene task was started
3089 """
3090 def __init__(self, task, stats, rq, noexec=False):
3091 sceneQueueEvent.__init__(self, task, stats, rq)
3092 self.noexec = noexec
3093
3094class runQueueTaskFailed(runQueueEvent):
3095 """
3096 Event notifying a task failed
3097 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003098 def __init__(self, task, stats, exitcode, rq, fakeroot_log=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003099 runQueueEvent.__init__(self, task, stats, rq)
3100 self.exitcode = exitcode
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003101 self.fakeroot_log = fakeroot_log
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003102
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003103 def __str__(self):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003104 if self.fakeroot_log:
3105 return "Task (%s) failed with exit code '%s' \nPseudo log:\n%s" % (self.taskstring, self.exitcode, self.fakeroot_log)
3106 else:
3107 return "Task (%s) failed with exit code '%s'" % (self.taskstring, self.exitcode)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003108
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003109class sceneQueueTaskFailed(sceneQueueEvent):
3110 """
3111 Event notifying a setscene task failed
3112 """
3113 def __init__(self, task, stats, exitcode, rq):
3114 sceneQueueEvent.__init__(self, task, stats, rq)
3115 self.exitcode = exitcode
3116
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003117 def __str__(self):
3118 return "Setscene task (%s) failed with exit code '%s' - real task will be run instead" % (self.taskstring, self.exitcode)
3119
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003120class sceneQueueComplete(sceneQueueEvent):
3121 """
3122 Event when all the sceneQueue tasks are complete
3123 """
3124 def __init__(self, stats, rq):
3125 self.stats = stats.copy()
3126 bb.event.Event.__init__(self)
3127
3128class runQueueTaskCompleted(runQueueEvent):
3129 """
3130 Event notifying a task completed
3131 """
3132
3133class sceneQueueTaskCompleted(sceneQueueEvent):
3134 """
3135 Event notifying a setscene task completed
3136 """
3137
3138class runQueueTaskSkipped(runQueueEvent):
3139 """
3140 Event notifying a task was skipped
3141 """
3142 def __init__(self, task, stats, rq, reason):
3143 runQueueEvent.__init__(self, task, stats, rq)
3144 self.reason = reason
3145
Brad Bishop08902b02019-08-20 09:16:51 -04003146class taskUniHashUpdate(bb.event.Event):
3147 """
3148 Base runQueue event class
3149 """
3150 def __init__(self, task, unihash):
3151 self.taskid = task
3152 self.unihash = unihash
3153 bb.event.Event.__init__(self)
3154
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003155class runQueuePipe():
3156 """
3157 Abstraction for a pipe between a worker thread and the server
3158 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003159 def __init__(self, pipein, pipeout, d, rq, rqexec, fakerootlogs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003160 self.input = pipein
3161 if pipeout:
3162 pipeout.close()
3163 bb.utils.nonblockingfd(self.input)
Andrew Geissler220dafd2023-10-04 10:18:08 -05003164 self.queue = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003165 self.d = d
3166 self.rq = rq
3167 self.rqexec = rqexec
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003168 self.fakerootlogs = fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003169
3170 def setrunqueueexec(self, rqexec):
3171 self.rqexec = rqexec
3172
3173 def read(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003174 for workers, name in [(self.rq.worker, "Worker"), (self.rq.fakeworker, "Fakeroot")]:
3175 for worker in workers.values():
3176 worker.process.poll()
3177 if worker.process.returncode is not None and not self.rq.teardown:
3178 bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, worker.process.pid, str(worker.process.returncode)))
3179 self.rq.finish_runqueue(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003180
3181 start = len(self.queue)
3182 try:
Andrew Geissler220dafd2023-10-04 10:18:08 -05003183 self.queue.extend(self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003184 except (OSError, IOError) as e:
3185 if e.errno != errno.EAGAIN:
3186 raise
3187 end = len(self.queue)
3188 found = True
Andrew Geissler595f6302022-01-24 19:11:47 +00003189 while found and self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003190 found = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003191 index = self.queue.find(b"</event>")
3192 while index != -1 and self.queue.startswith(b"<event>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003193 try:
3194 event = pickle.loads(self.queue[7:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003195 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
3196 if isinstance(e, pickle.UnpicklingError) and "truncated" in str(e):
3197 # The pickled data could contain "</event>" so search for the next occurance
3198 # unpickling again, this should be the only way an unpickle error could occur
3199 index = self.queue.find(b"</event>", index + 1)
3200 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003201 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
3202 bb.event.fire_from_worker(event, self.d)
Brad Bishop08902b02019-08-20 09:16:51 -04003203 if isinstance(event, taskUniHashUpdate):
3204 self.rqexec.updated_taskhash_queue.append((event.taskid, event.unihash))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003205 found = True
3206 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003207 index = self.queue.find(b"</event>")
3208 index = self.queue.find(b"</exitcode>")
3209 while index != -1 and self.queue.startswith(b"<exitcode>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003210 try:
3211 task, status = pickle.loads(self.queue[10:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003212 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003213 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003214 (_, _, _, taskfn) = split_tid_mcfn(task)
3215 fakerootlog = None
3216 if self.fakerootlogs and taskfn and taskfn in self.fakerootlogs:
3217 fakerootlog = self.fakerootlogs[taskfn]
3218 self.rqexec.runqueue_process_waitpid(task, status, fakerootlog=fakerootlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003219 found = True
3220 self.queue = self.queue[index+11:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003221 index = self.queue.find(b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003222 return (end > start)
3223
3224 def close(self):
3225 while self.read():
3226 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00003227 if self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003228 print("Warning, worker left partial message: %s" % self.queue)
3229 self.input.close()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003230
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003231def get_setscene_enforce_ignore_tasks(d, targets):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003232 if d.getVar('BB_SETSCENE_ENFORCE') != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003233 return None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003234 ignore_tasks = (d.getVar("BB_SETSCENE_ENFORCE_IGNORE_TASKS") or "").split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003235 outlist = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003236 for item in ignore_tasks[:]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003237 if item.startswith('%:'):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003238 for (mc, target, task, fn) in targets:
3239 outlist.append(target + ':' + item.split(':')[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003240 else:
3241 outlist.append(item)
3242 return outlist
3243
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003244def check_setscene_enforce_ignore_tasks(pn, taskname, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003245 import fnmatch
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003246 if ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003247 item = '%s:%s' % (pn, taskname)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003248 for ignore_tasks in ignore_tasks:
3249 if fnmatch.fnmatch(item, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003250 return True
3251 return False
3252 return True