blob: 5a45943e3a0c780e08aa046d0c17ffd6ffca2521 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""
2BitBake 'RunQueue' implementation
3
4Handles preparation and execution of a queue of tasks
5"""
6
7# Copyright (C) 2006-2007 Richard Purdie
8#
Brad Bishopc342db32019-05-15 21:57:59 -04009# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011
12import copy
13import os
14import sys
Patrick Williamsc124f4f2015-09-15 14:41:29 -050015import stat
Patrick Williamsc124f4f2015-09-15 14:41:29 -050016import errno
17import logging
18import re
19import bb
Andrew Geissler82c905d2020-04-13 13:39:40 -050020from bb import msg, event
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021from bb import monitordisk
22import subprocess
Patrick Williamsc0f7c042017-02-23 20:41:17 -060023import pickle
Brad Bishop6e60e8b2018-02-01 10:27:11 -050024from multiprocessing import Process
Brad Bishop19323692019-04-05 15:28:33 -040025import shlex
Brad Bishop96ff1982019-08-19 13:50:42 -040026import pprint
Patrick Williamsdb4c27e2022-08-05 08:10:29 -050027import time
Patrick Williamsc124f4f2015-09-15 14:41:29 -050028
29bblogger = logging.getLogger("BitBake")
30logger = logging.getLogger("BitBake.RunQueue")
Andrew Geissler82c905d2020-04-13 13:39:40 -050031hashequiv_logger = logging.getLogger("BitBake.RunQueue.HashEquiv")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032
Brad Bishop19323692019-04-05 15:28:33 -040033__find_sha256__ = re.compile( r'(?i)(?<![a-z0-9])[a-f0-9]{64}(?![a-z0-9])' )
Patrick Williamsc124f4f2015-09-15 14:41:29 -050034
Patrick Williamsc0f7c042017-02-23 20:41:17 -060035def fn_from_tid(tid):
36 return tid.rsplit(":", 1)[0]
37
38def taskname_from_tid(tid):
39 return tid.rsplit(":", 1)[1]
40
Andrew Geissler99467da2019-02-25 18:54:23 -060041def mc_from_tid(tid):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060042 if tid.startswith('mc:') and tid.count(':') >= 2:
Andrew Geissler99467da2019-02-25 18:54:23 -060043 return tid.split(':')[1]
44 return ""
45
Patrick Williamsc0f7c042017-02-23 20:41:17 -060046def split_tid(tid):
47 (mc, fn, taskname, _) = split_tid_mcfn(tid)
48 return (mc, fn, taskname)
49
Andrew Geissler5a43b432020-06-13 10:46:56 -050050def split_mc(n):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060051 if n.startswith("mc:") and n.count(':') >= 2:
Andrew Geissler5a43b432020-06-13 10:46:56 -050052 _, mc, n = n.split(":", 2)
53 return (mc, n)
54 return ('', n)
55
Patrick Williamsc0f7c042017-02-23 20:41:17 -060056def split_tid_mcfn(tid):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060057 if tid.startswith('mc:') and tid.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060058 elems = tid.split(':')
59 mc = elems[1]
60 fn = ":".join(elems[2:-1])
61 taskname = elems[-1]
Brad Bishop15ae2502019-06-18 21:44:24 -040062 mcfn = "mc:" + mc + ":" + fn
Patrick Williamsc0f7c042017-02-23 20:41:17 -060063 else:
64 tid = tid.rsplit(":", 1)
65 mc = ""
66 fn = tid[0]
67 taskname = tid[1]
68 mcfn = fn
69
70 return (mc, fn, taskname, mcfn)
71
72def build_tid(mc, fn, taskname):
73 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -040074 return "mc:" + mc + ":" + fn + ":" + taskname
Patrick Williamsc0f7c042017-02-23 20:41:17 -060075 return fn + ":" + taskname
76
Brad Bishop96ff1982019-08-19 13:50:42 -040077# Index used to pair up potentially matching multiconfig tasks
78# We match on PN, taskname and hash being equal
79def pending_hash_index(tid, rqdata):
80 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
81 pn = rqdata.dataCaches[mc].pkg_fn[taskfn]
Brad Bishop00e122a2019-10-05 11:10:57 -040082 h = rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -040083 return pn + ":" + "taskname" + h
84
Patrick Williamsc124f4f2015-09-15 14:41:29 -050085class RunQueueStats:
86 """
87 Holds statistics on the tasks handled by the associated runQueue
88 """
Andrew Geissler5199d832021-09-24 16:47:35 -050089 def __init__(self, total, setscene_total):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090 self.completed = 0
91 self.skipped = 0
92 self.failed = 0
93 self.active = 0
Andrew Geissler5199d832021-09-24 16:47:35 -050094 self.setscene_active = 0
95 self.setscene_covered = 0
96 self.setscene_notcovered = 0
97 self.setscene_total = setscene_total
Patrick Williamsc124f4f2015-09-15 14:41:29 -050098 self.total = total
99
100 def copy(self):
Andrew Geissler5199d832021-09-24 16:47:35 -0500101 obj = self.__class__(self.total, self.setscene_total)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102 obj.__dict__.update(self.__dict__)
103 return obj
104
105 def taskFailed(self):
106 self.active = self.active - 1
107 self.failed = self.failed + 1
108
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800109 def taskCompleted(self):
110 self.active = self.active - 1
111 self.completed = self.completed + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500112
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800113 def taskSkipped(self):
114 self.active = self.active + 1
115 self.skipped = self.skipped + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500116
117 def taskActive(self):
118 self.active = self.active + 1
119
Andrew Geissler5199d832021-09-24 16:47:35 -0500120 def updateCovered(self, covered, notcovered):
121 self.setscene_covered = covered
122 self.setscene_notcovered = notcovered
123
124 def updateActiveSetscene(self, active):
125 self.setscene_active = active
126
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127# These values indicate the next step due to be run in the
128# runQueue state machine
129runQueuePrepare = 2
130runQueueSceneInit = 3
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131runQueueRunning = 6
132runQueueFailed = 7
133runQueueCleanUp = 8
134runQueueComplete = 9
135
136class RunQueueScheduler(object):
137 """
138 Control the order tasks are scheduled in.
139 """
140 name = "basic"
141
142 def __init__(self, runqueue, rqdata):
143 """
144 The default scheduler just returns the first buildable task (the
145 priority map is sorted by task number)
146 """
147 self.rq = runqueue
148 self.rqdata = rqdata
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600149 self.numTasks = len(self.rqdata.runtaskentries)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600151 self.prio_map = [self.rqdata.runtaskentries.keys()]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500152
Brad Bishop08902b02019-08-20 09:16:51 -0400153 self.buildable = set()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800154 self.skip_maxthread = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500155 self.stamps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600156 for tid in self.rqdata.runtaskentries:
157 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -0600158 self.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600159 if tid in self.rq.runq_buildable:
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600160 self.buildable.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500161
162 self.rev_prio_map = None
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500163 self.is_pressure_usable()
164
165 def is_pressure_usable(self):
166 """
167 If monitoring pressure, return True if pressure files can be open and read. For example
168 openSUSE /proc/pressure/* files have readable file permissions but when read the error EOPNOTSUPP (Operation not supported)
169 is returned.
170 """
Patrick Williams92b42cb2022-09-03 06:53:57 -0500171 if self.rq.max_cpu_pressure or self.rq.max_io_pressure or self.rq.max_memory_pressure:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500172 try:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500173 with open("/proc/pressure/cpu") as cpu_pressure_fds, \
174 open("/proc/pressure/io") as io_pressure_fds, \
175 open("/proc/pressure/memory") as memory_pressure_fds:
176
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500177 self.prev_cpu_pressure = cpu_pressure_fds.readline().split()[4].split("=")[1]
178 self.prev_io_pressure = io_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williams92b42cb2022-09-03 06:53:57 -0500179 self.prev_memory_pressure = memory_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500180 self.prev_pressure_time = time.time()
181 self.check_pressure = True
182 except:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500183 bb.note("The /proc/pressure files can't be read. Continuing build without monitoring pressure")
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500184 self.check_pressure = False
185 else:
186 self.check_pressure = False
187
188 def exceeds_max_pressure(self):
189 """
190 Monitor the difference in total pressure at least once per second, if
Patrick Williams92b42cb2022-09-03 06:53:57 -0500191 BB_PRESSURE_MAX_{CPU|IO|MEMORY} are set, return True if above threshold.
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500192 """
193 if self.check_pressure:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500194 with open("/proc/pressure/cpu") as cpu_pressure_fds, \
195 open("/proc/pressure/io") as io_pressure_fds, \
196 open("/proc/pressure/memory") as memory_pressure_fds:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500197 # extract "total" from /proc/pressure/{cpu|io}
198 curr_cpu_pressure = cpu_pressure_fds.readline().split()[4].split("=")[1]
199 curr_io_pressure = io_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williams92b42cb2022-09-03 06:53:57 -0500200 curr_memory_pressure = memory_pressure_fds.readline().split()[4].split("=")[1]
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500201 now = time.time()
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500202 tdiff = now - self.prev_pressure_time
Andrew Geissler5082cc72023-09-11 08:41:39 -0400203 psi_accumulation_interval = 1.0
204 cpu_pressure = (float(curr_cpu_pressure) - float(self.prev_cpu_pressure)) / tdiff
205 io_pressure = (float(curr_io_pressure) - float(self.prev_io_pressure)) / tdiff
206 memory_pressure = (float(curr_memory_pressure) - float(self.prev_memory_pressure)) / tdiff
207 exceeds_cpu_pressure = self.rq.max_cpu_pressure and cpu_pressure > self.rq.max_cpu_pressure
208 exceeds_io_pressure = self.rq.max_io_pressure and io_pressure > self.rq.max_io_pressure
209 exceeds_memory_pressure = self.rq.max_memory_pressure and memory_pressure > self.rq.max_memory_pressure
210
211 if tdiff > psi_accumulation_interval:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500212 self.prev_cpu_pressure = curr_cpu_pressure
213 self.prev_io_pressure = curr_io_pressure
Patrick Williams92b42cb2022-09-03 06:53:57 -0500214 self.prev_memory_pressure = curr_memory_pressure
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500215 self.prev_pressure_time = now
Andrew Geissler5082cc72023-09-11 08:41:39 -0400216
Andrew Geissler8f840682023-07-21 09:09:43 -0500217 pressure_state = (exceeds_cpu_pressure, exceeds_io_pressure, exceeds_memory_pressure)
Andrew Geissler5082cc72023-09-11 08:41:39 -0400218 pressure_values = (round(cpu_pressure,1), self.rq.max_cpu_pressure, round(io_pressure,1), self.rq.max_io_pressure, round(memory_pressure,1), self.rq.max_memory_pressure)
Andrew Geissler8f840682023-07-21 09:09:43 -0500219 if hasattr(self, "pressure_state") and pressure_state != self.pressure_state:
Andrew Geissler5082cc72023-09-11 08:41:39 -0400220 bb.note("Pressure status changed to CPU: %s, IO: %s, Mem: %s (CPU: %s/%s, IO: %s/%s, Mem: %s/%s) - using %s/%s bitbake threads" % (pressure_state + pressure_values + (len(self.rq.runq_running.difference(self.rq.runq_complete)), self.rq.number_tasks)))
Andrew Geissler8f840682023-07-21 09:09:43 -0500221 self.pressure_state = pressure_state
Patrick Williams92b42cb2022-09-03 06:53:57 -0500222 return (exceeds_cpu_pressure or exceeds_io_pressure or exceeds_memory_pressure)
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500223 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500224
225 def next_buildable_task(self):
226 """
227 Return the id of the first task we find that is buildable
228 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500229 # Once tasks are running we don't need to worry about them again
230 self.buildable.difference_update(self.rq.runq_running)
Brad Bishop08902b02019-08-20 09:16:51 -0400231 buildable = set(self.buildable)
Brad Bishop08902b02019-08-20 09:16:51 -0400232 buildable.difference_update(self.rq.holdoff_tasks)
233 buildable.intersection_update(self.rq.tasks_covered | self.rq.tasks_notcovered)
Brad Bishop96ff1982019-08-19 13:50:42 -0400234 if not buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500235 return None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800236
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500237 # Bitbake requires that at least one task be active. Only check for pressure if
238 # this is the case, otherwise the pressure limitation could result in no tasks
239 # being active and no new tasks started thereby, at times, breaking the scheduler.
240 if self.rq.stats.active and self.exceeds_max_pressure():
241 return None
242
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800243 # Filter out tasks that have a max number of threads that have been exceeded
244 skip_buildable = {}
245 for running in self.rq.runq_running.difference(self.rq.runq_complete):
246 rtaskname = taskname_from_tid(running)
247 if rtaskname not in self.skip_maxthread:
248 self.skip_maxthread[rtaskname] = self.rq.cfgData.getVarFlag(rtaskname, "number_threads")
249 if not self.skip_maxthread[rtaskname]:
250 continue
251 if rtaskname in skip_buildable:
252 skip_buildable[rtaskname] += 1
253 else:
254 skip_buildable[rtaskname] = 1
255
Brad Bishop96ff1982019-08-19 13:50:42 -0400256 if len(buildable) == 1:
Brad Bishop08902b02019-08-20 09:16:51 -0400257 tid = buildable.pop()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800258 taskname = taskname_from_tid(tid)
259 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
260 return None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600261 stamp = self.stamps[tid]
262 if stamp not in self.rq.build_stamps.values():
263 return tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500264
265 if not self.rev_prio_map:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600266 self.rev_prio_map = {}
267 for tid in self.rqdata.runtaskentries:
268 self.rev_prio_map[tid] = self.prio_map.index(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500269
270 best = None
271 bestprio = None
Brad Bishop96ff1982019-08-19 13:50:42 -0400272 for tid in buildable:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800273 taskname = taskname_from_tid(tid)
274 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
275 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600276 prio = self.rev_prio_map[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500277 if bestprio is None or bestprio > prio:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600278 stamp = self.stamps[tid]
279 if stamp in self.rq.build_stamps.values():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500280 continue
281 bestprio = prio
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600282 best = tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283
284 return best
285
286 def next(self):
287 """
288 Return the id of the task we should build next
289 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800290 if self.rq.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500291 return self.next_buildable_task()
292
Brad Bishop316dfdd2018-06-25 12:45:53 -0400293 def newbuildable(self, task):
Brad Bishop08902b02019-08-20 09:16:51 -0400294 self.buildable.add(task)
295
296 def removebuildable(self, task):
297 self.buildable.remove(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500298
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500299 def describe_task(self, taskid):
300 result = 'ID %s' % taskid
301 if self.rev_prio_map:
302 result = result + (' pri %d' % self.rev_prio_map[taskid])
303 return result
304
305 def dump_prio(self, comment):
306 bb.debug(3, '%s (most important first):\n%s' %
307 (comment,
308 '\n'.join(['%d. %s' % (index + 1, self.describe_task(taskid)) for
309 index, taskid in enumerate(self.prio_map)])))
310
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311class RunQueueSchedulerSpeed(RunQueueScheduler):
312 """
313 A scheduler optimised for speed. The priority map is sorted by task weight,
314 heavier weighted tasks (tasks needed by the most other tasks) are run first.
315 """
316 name = "speed"
317
318 def __init__(self, runqueue, rqdata):
319 """
320 The priority map is sorted by task weight.
321 """
322 RunQueueScheduler.__init__(self, runqueue, rqdata)
323
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600324 weights = {}
325 for tid in self.rqdata.runtaskentries:
326 weight = self.rqdata.runtaskentries[tid].weight
327 if not weight in weights:
328 weights[weight] = []
329 weights[weight].append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500330
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600331 self.prio_map = []
332 for weight in sorted(weights):
333 for w in weights[weight]:
334 self.prio_map.append(w)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500335
336 self.prio_map.reverse()
337
338class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed):
339 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500340 A scheduler optimised to complete .bb files as quickly as possible. The
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500341 priority map is sorted by task weight, but then reordered so once a given
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500342 .bb file starts to build, it's completed as quickly as possible by
343 running all tasks related to the same .bb file one after the after.
344 This works well where disk space is at a premium and classes like OE's
345 rm_work are in force.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500346 """
347 name = "completion"
348
349 def __init__(self, runqueue, rqdata):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500350 super(RunQueueSchedulerCompletion, self).__init__(runqueue, rqdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500352 # Extract list of tasks for each recipe, with tasks sorted
353 # ascending from "must run first" (typically do_fetch) to
354 # "runs last" (do_build). The speed scheduler prioritizes
355 # tasks that must run first before the ones that run later;
356 # this is what we depend on here.
357 task_lists = {}
358 for taskid in self.prio_map:
359 fn, taskname = taskid.rsplit(':', 1)
360 task_lists.setdefault(fn, []).append(taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500361
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500362 # Now unify the different task lists. The strategy is that
363 # common tasks get skipped and new ones get inserted after the
364 # preceeding common one(s) as they are found. Because task
365 # lists should differ only by their number of tasks, but not
366 # the ordering of the common tasks, this should result in a
367 # deterministic result that is a superset of the individual
368 # task ordering.
369 all_tasks = []
370 for recipe, new_tasks in task_lists.items():
371 index = 0
372 old_task = all_tasks[index] if index < len(all_tasks) else None
373 for new_task in new_tasks:
374 if old_task == new_task:
375 # Common task, skip it. This is the fast-path which
376 # avoids a full search.
377 index += 1
378 old_task = all_tasks[index] if index < len(all_tasks) else None
379 else:
380 try:
381 index = all_tasks.index(new_task)
382 # Already present, just not at the current
383 # place. We re-synchronized by changing the
384 # index so that it matches again. Now
385 # move on to the next existing task.
386 index += 1
387 old_task = all_tasks[index] if index < len(all_tasks) else None
388 except ValueError:
389 # Not present. Insert before old_task, which
390 # remains the same (but gets shifted back).
391 all_tasks.insert(index, new_task)
392 index += 1
393 bb.debug(3, 'merged task list: %s' % all_tasks)
394
395 # Now reverse the order so that tasks that finish the work on one
396 # recipe are considered more imporant (= come first). The ordering
397 # is now so that do_build is most important.
398 all_tasks.reverse()
399
400 # Group tasks of the same kind before tasks of less important
401 # kinds at the head of the queue (because earlier = lower
402 # priority number = runs earlier), while preserving the
403 # ordering by recipe. If recipe foo is more important than
404 # bar, then the goal is to work on foo's do_populate_sysroot
405 # before bar's do_populate_sysroot and on the more important
406 # tasks of foo before any of the less important tasks in any
407 # other recipe (if those other recipes are more important than
408 # foo).
409 #
410 # All of this only applies when tasks are runable. Explicit
411 # dependencies still override this ordering by priority.
412 #
413 # Here's an example why this priority re-ordering helps with
414 # minimizing disk usage. Consider a recipe foo with a higher
415 # priority than bar where foo DEPENDS on bar. Then the
416 # implicit rule (from base.bbclass) is that foo's do_configure
417 # depends on bar's do_populate_sysroot. This ensures that
418 # bar's do_populate_sysroot gets done first. Normally the
419 # tasks from foo would continue to run once that is done, and
420 # bar only gets completed and cleaned up later. By ordering
421 # bar's task that depend on bar's do_populate_sysroot before foo's
422 # do_configure, that problem gets avoided.
423 task_index = 0
424 self.dump_prio('original priorities')
425 for task in all_tasks:
426 for index in range(task_index, self.numTasks):
427 taskid = self.prio_map[index]
428 taskname = taskid.rsplit(':', 1)[1]
429 if taskname == task:
430 del self.prio_map[index]
431 self.prio_map.insert(task_index, taskid)
432 task_index += 1
433 self.dump_prio('completion priorities')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500434
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600435class RunTaskEntry(object):
436 def __init__(self):
437 self.depends = set()
438 self.revdeps = set()
439 self.hash = None
Brad Bishop19323692019-04-05 15:28:33 -0400440 self.unihash = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600441 self.task = None
442 self.weight = 1
443
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444class RunQueueData:
445 """
446 BitBake Run Queue implementation
447 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600448 def __init__(self, rq, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500449 self.cooker = cooker
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600450 self.dataCaches = dataCaches
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500451 self.taskData = taskData
452 self.targets = targets
453 self.rq = rq
454 self.warn_multi_bb = False
455
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000456 self.multi_provider_allowed = (cfgData.getVar("BB_MULTI_PROVIDER_ALLOWED") or "").split()
457 self.setscene_ignore_tasks = get_setscene_enforce_ignore_tasks(cfgData, targets)
458 self.setscene_ignore_tasks_checked = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500459 self.setscene_enforce = (cfgData.getVar('BB_SETSCENE_ENFORCE') == "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600460 self.init_progress_reporter = bb.progress.DummyMultiStageProcessProgressReporter()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500461
462 self.reset()
463
464 def reset(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600465 self.runtaskentries = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500466
467 def runq_depends_names(self, ids):
468 import re
469 ret = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600470 for id in ids:
471 nam = os.path.basename(id)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500472 nam = re.sub("_[^,]*,", ",", nam)
473 ret.extend([nam])
474 return ret
475
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600476 def get_task_hash(self, tid):
477 return self.runtaskentries[tid].hash
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500478
Brad Bishop19323692019-04-05 15:28:33 -0400479 def get_task_unihash(self, tid):
480 return self.runtaskentries[tid].unihash
481
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600482 def get_user_idstring(self, tid, task_name_suffix = ""):
483 return tid + task_name_suffix
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500484
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500485 def get_short_user_idstring(self, task, task_name_suffix = ""):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500486 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
487 pn = self.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600488 taskname = taskname_from_tid(task) + task_name_suffix
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500489 return "%s:%s" % (pn, taskname)
490
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491 def circular_depchains_handler(self, tasks):
492 """
493 Some tasks aren't buildable, likely due to circular dependency issues.
494 Identify the circular dependencies and print them in a user readable format.
495 """
496 from copy import deepcopy
497
498 valid_chains = []
499 explored_deps = {}
500 msgs = []
501
Andrew Geissler99467da2019-02-25 18:54:23 -0600502 class TooManyLoops(Exception):
503 pass
504
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500505 def chain_reorder(chain):
506 """
507 Reorder a dependency chain so the lowest task id is first
508 """
509 lowest = 0
510 new_chain = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600511 for entry in range(len(chain)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500512 if chain[entry] < chain[lowest]:
513 lowest = entry
514 new_chain.extend(chain[lowest:])
515 new_chain.extend(chain[:lowest])
516 return new_chain
517
518 def chain_compare_equal(chain1, chain2):
519 """
520 Compare two dependency chains and see if they're the same
521 """
522 if len(chain1) != len(chain2):
523 return False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600524 for index in range(len(chain1)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500525 if chain1[index] != chain2[index]:
526 return False
527 return True
528
529 def chain_array_contains(chain, chain_array):
530 """
531 Return True if chain_array contains chain
532 """
533 for ch in chain_array:
534 if chain_compare_equal(ch, chain):
535 return True
536 return False
537
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600538 def find_chains(tid, prev_chain):
539 prev_chain.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500540 total_deps = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600541 total_deps.extend(self.runtaskentries[tid].revdeps)
542 for revdep in self.runtaskentries[tid].revdeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500543 if revdep in prev_chain:
544 idx = prev_chain.index(revdep)
545 # To prevent duplicates, reorder the chain to start with the lowest taskid
546 # and search through an array of those we've already printed
547 chain = prev_chain[idx:]
548 new_chain = chain_reorder(chain)
549 if not chain_array_contains(new_chain, valid_chains):
550 valid_chains.append(new_chain)
551 msgs.append("Dependency loop #%d found:\n" % len(valid_chains))
552 for dep in new_chain:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600553 msgs.append(" Task %s (dependent Tasks %s)\n" % (dep, self.runq_depends_names(self.runtaskentries[dep].depends)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500554 msgs.append("\n")
555 if len(valid_chains) > 10:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000556 msgs.append("Halted dependency loops search after 10 matches.\n")
Andrew Geissler99467da2019-02-25 18:54:23 -0600557 raise TooManyLoops
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500558 continue
559 scan = False
560 if revdep not in explored_deps:
561 scan = True
562 elif revdep in explored_deps[revdep]:
563 scan = True
564 else:
565 for dep in prev_chain:
566 if dep in explored_deps[revdep]:
567 scan = True
568 if scan:
569 find_chains(revdep, copy.deepcopy(prev_chain))
570 for dep in explored_deps[revdep]:
571 if dep not in total_deps:
572 total_deps.append(dep)
573
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600574 explored_deps[tid] = total_deps
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500575
Andrew Geissler99467da2019-02-25 18:54:23 -0600576 try:
577 for task in tasks:
578 find_chains(task, [])
579 except TooManyLoops:
580 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500581
582 return msgs
583
584 def calculate_task_weights(self, endpoints):
585 """
586 Calculate a number representing the "weight" of each task. Heavier weighted tasks
587 have more dependencies and hence should be executed sooner for maximum speed.
588
589 This function also sanity checks the task list finding tasks that are not
590 possible to execute due to circular dependencies.
591 """
592
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600593 numTasks = len(self.runtaskentries)
594 weight = {}
595 deps_left = {}
596 task_done = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600598 for tid in self.runtaskentries:
599 task_done[tid] = False
600 weight[tid] = 1
601 deps_left[tid] = len(self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600603 for tid in endpoints:
604 weight[tid] = 10
605 task_done[tid] = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500606
607 while True:
608 next_points = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600609 for tid in endpoints:
610 for revdep in self.runtaskentries[tid].depends:
611 weight[revdep] = weight[revdep] + weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500612 deps_left[revdep] = deps_left[revdep] - 1
613 if deps_left[revdep] == 0:
614 next_points.append(revdep)
615 task_done[revdep] = True
616 endpoints = next_points
Andrew Geissler595f6302022-01-24 19:11:47 +0000617 if not next_points:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500618 break
619
620 # Circular dependency sanity check
621 problem_tasks = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600622 for tid in self.runtaskentries:
623 if task_done[tid] is False or deps_left[tid] != 0:
624 problem_tasks.append(tid)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600625 logger.debug2("Task %s is not buildable", tid)
626 logger.debug2("(Complete marker was %s and the remaining dependency count was %s)\n", task_done[tid], deps_left[tid])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600627 self.runtaskentries[tid].weight = weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500628
629 if problem_tasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600630 message = "%s unbuildable tasks were found.\n" % len(problem_tasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631 message = message + "These are usually caused by circular dependencies and any circular dependency chains found will be printed below. Increase the debug level to see a list of unbuildable tasks.\n\n"
632 message = message + "Identifying dependency loops (this may take a short while)...\n"
633 logger.error(message)
634
635 msgs = self.circular_depchains_handler(problem_tasks)
636
637 message = "\n"
638 for msg in msgs:
639 message = message + msg
640 bb.msg.fatal("RunQueue", message)
641
642 return weight
643
644 def prepare(self):
645 """
646 Turn a set of taskData into a RunQueue and compute data needed
647 to optimise the execution order.
648 """
649
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600650 runq_build = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 recursivetasks = {}
652 recursiveitasks = {}
653 recursivetasksselfref = set()
654
655 taskData = self.taskData
656
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600657 found = False
658 for mc in self.taskData:
Andrew Geissler595f6302022-01-24 19:11:47 +0000659 if taskData[mc].taskentries:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600660 found = True
661 break
662 if not found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663 # Nothing to do
664 return 0
665
Andrew Geissler517393d2023-01-13 08:55:19 -0600666 bb.parse.siggen.setup_datacache(self.dataCaches)
667
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600668 self.init_progress_reporter.start()
669 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600670 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500671
672 # Step A - Work out a list of tasks to run
673 #
674 # Taskdata gives us a list of possible providers for every build and run
675 # target ordered by priority. It also gives information on each of those
676 # providers.
677 #
678 # To create the actual list of tasks to execute we fix the list of
679 # providers and then resolve the dependencies into task IDs. This
680 # process is repeated for each type of dependency (tdepends, deptask,
681 # rdeptast, recrdeptask, idepends).
682
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600683 def add_build_dependencies(depids, tasknames, depends, mc):
684 for depname in depids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500685 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600686 if depname not in taskData[mc].build_targets or not taskData[mc].build_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500687 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600688 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689 if depdata is None:
690 continue
691 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600692 t = depdata + ":" + taskname
693 if t in taskData[mc].taskentries:
694 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500695
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600696 def add_runtime_dependencies(depids, tasknames, depends, mc):
697 for depname in depids:
698 if depname not in taskData[mc].run_targets or not taskData[mc].run_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500699 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600700 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500701 if depdata is None:
702 continue
703 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600704 t = depdata + ":" + taskname
705 if t in taskData[mc].taskentries:
706 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800708 def add_mc_dependencies(mc, tid):
709 mcdeps = taskData[mc].get_mcdepends()
710 for dep in mcdeps:
711 mcdependency = dep.split(':')
712 pn = mcdependency[3]
713 frommc = mcdependency[1]
714 mcdep = mcdependency[2]
715 deptask = mcdependency[4]
Andrew Geissler517393d2023-01-13 08:55:19 -0600716 if mcdep not in taskData:
717 bb.fatal("Multiconfig '%s' is referenced in multiconfig dependency '%s' but not enabled in BBMULTICONFIG?" % (mcdep, dep))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800718 if mc == frommc:
719 fn = taskData[mcdep].build_targets[pn][0]
720 newdep = '%s:%s' % (fn,deptask)
721 taskData[mc].taskentries[tid].tdepends.append(newdep)
722
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 for mc in taskData:
724 for tid in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600726 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
727 #runtid = build_tid(mc, fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500728
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600729 #logger.debug2("Processing %s,%s:%s", mc, fn, taskname)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600730
731 depends = set()
732 task_deps = self.dataCaches[mc].task_deps[taskfn]
733
734 self.runtaskentries[tid] = RunTaskEntry()
735
736 if fn in taskData[mc].failed_fns:
737 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500738
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800739 # We add multiconfig dependencies before processing internal task deps (tdepends)
740 if 'mcdepends' in task_deps and taskname in task_deps['mcdepends']:
741 add_mc_dependencies(mc, tid)
742
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500743 # Resolve task internal dependencies
744 #
745 # e.g. addtask before X after Y
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600746 for t in taskData[mc].taskentries[tid].tdepends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800747 (depmc, depfn, deptaskname, _) = split_tid_mcfn(t)
748 depends.add(build_tid(depmc, depfn, deptaskname))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749
750 # Resolve 'deptask' dependencies
751 #
752 # e.g. do_sometask[deptask] = "do_someothertask"
753 # (makes sure sometask runs after someothertask of all DEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600754 if 'deptask' in task_deps and taskname in task_deps['deptask']:
755 tasknames = task_deps['deptask'][taskname].split()
756 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500757
758 # Resolve 'rdeptask' dependencies
759 #
760 # e.g. do_sometask[rdeptask] = "do_someothertask"
761 # (makes sure sometask runs after someothertask of all RDEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600762 if 'rdeptask' in task_deps and taskname in task_deps['rdeptask']:
763 tasknames = task_deps['rdeptask'][taskname].split()
764 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500765
766 # Resolve inter-task dependencies
767 #
768 # e.g. do_sometask[depends] = "targetname:do_someothertask"
769 # (makes sure sometask runs after targetname's someothertask)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600770 idepends = taskData[mc].taskentries[tid].idepends
771 for (depname, idependtask) in idepends:
772 if depname in taskData[mc].build_targets and taskData[mc].build_targets[depname] and not depname in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500773 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600774 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600776 t = depdata + ":" + idependtask
777 depends.add(t)
778 if t not in taskData[mc].taskentries:
779 bb.msg.fatal("RunQueue", "Task %s in %s depends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
780 irdepends = taskData[mc].taskentries[tid].irdepends
781 for (depname, idependtask) in irdepends:
782 if depname in taskData[mc].run_targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500783 # Won't be in run_targets if ASSUME_PROVIDED
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500784 if not taskData[mc].run_targets[depname]:
785 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600786 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600788 t = depdata + ":" + idependtask
789 depends.add(t)
790 if t not in taskData[mc].taskentries:
791 bb.msg.fatal("RunQueue", "Task %s in %s rdepends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500792
793 # Resolve recursive 'recrdeptask' dependencies (Part A)
794 #
795 # e.g. do_sometask[recrdeptask] = "do_someothertask"
796 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
797 # We cover the recursive part of the dependencies below
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 if 'recrdeptask' in task_deps and taskname in task_deps['recrdeptask']:
799 tasknames = task_deps['recrdeptask'][taskname].split()
800 recursivetasks[tid] = tasknames
801 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
802 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
803 if taskname in tasknames:
804 recursivetasksselfref.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500805
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600806 if 'recideptask' in task_deps and taskname in task_deps['recideptask']:
807 recursiveitasks[tid] = []
808 for t in task_deps['recideptask'][taskname].split():
809 newdep = build_tid(mc, fn, t)
810 recursiveitasks[tid].append(newdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500811
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600812 self.runtaskentries[tid].depends = depends
Brad Bishop316dfdd2018-06-25 12:45:53 -0400813 # Remove all self references
814 self.runtaskentries[tid].depends.discard(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500815
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600816 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500817
Brad Bishop316dfdd2018-06-25 12:45:53 -0400818 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600819 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400820
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821 # Resolve recursive 'recrdeptask' dependencies (Part B)
822 #
823 # e.g. do_sometask[recrdeptask] = "do_someothertask"
824 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600825 # We need to do this separately since we need all of runtaskentries[*].depends to be complete before this is processed
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600826
Brad Bishop316dfdd2018-06-25 12:45:53 -0400827 # Generating/interating recursive lists of dependencies is painful and potentially slow
828 # Precompute recursive task dependencies here by:
829 # a) create a temp list of reverse dependencies (revdeps)
830 # b) walk up the ends of the chains (when a given task no longer has dependencies i.e. len(deps) == 0)
831 # c) combine the total list of dependencies in cumulativedeps
832 # d) optimise by pre-truncating 'task' off the items in cumulativedeps (keeps items in sets lower)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834
Brad Bishop316dfdd2018-06-25 12:45:53 -0400835 revdeps = {}
836 deps = {}
837 cumulativedeps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600838 for tid in self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400839 deps[tid] = set(self.runtaskentries[tid].depends)
840 revdeps[tid] = set()
841 cumulativedeps[tid] = set()
842 # Generate a temp list of reverse dependencies
843 for tid in self.runtaskentries:
844 for dep in self.runtaskentries[tid].depends:
845 revdeps[dep].add(tid)
846 # Find the dependency chain endpoints
847 endpoints = set()
848 for tid in self.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +0000849 if not deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400850 endpoints.add(tid)
851 # Iterate the chains collating dependencies
852 while endpoints:
853 next = set()
854 for tid in endpoints:
855 for dep in revdeps[tid]:
856 cumulativedeps[dep].add(fn_from_tid(tid))
857 cumulativedeps[dep].update(cumulativedeps[tid])
858 if tid in deps[dep]:
859 deps[dep].remove(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +0000860 if not deps[dep]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400861 next.add(dep)
862 endpoints = next
863 #for tid in deps:
Andrew Geissler595f6302022-01-24 19:11:47 +0000864 # if deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400865 # bb.warn("Sanity test failure, dependencies left for %s (%s)" % (tid, deps[tid]))
866
867 # Loop here since recrdeptasks can depend upon other recrdeptasks and we have to
868 # resolve these recursively until we aren't adding any further extra dependencies
869 extradeps = True
870 while extradeps:
871 extradeps = 0
872 for tid in recursivetasks:
873 tasknames = recursivetasks[tid]
874
875 totaldeps = set(self.runtaskentries[tid].depends)
876 if tid in recursiveitasks:
877 totaldeps.update(recursiveitasks[tid])
878 for dep in recursiveitasks[tid]:
879 if dep not in self.runtaskentries:
880 continue
881 totaldeps.update(self.runtaskentries[dep].depends)
882
883 deps = set()
884 for dep in totaldeps:
885 if dep in cumulativedeps:
886 deps.update(cumulativedeps[dep])
887
888 for t in deps:
889 for taskname in tasknames:
890 newtid = t + ":" + taskname
891 if newtid == tid:
892 continue
893 if newtid in self.runtaskentries and newtid not in self.runtaskentries[tid].depends:
894 extradeps += 1
895 self.runtaskentries[tid].depends.add(newtid)
896
897 # Handle recursive tasks which depend upon other recursive tasks
898 deps = set()
899 for dep in self.runtaskentries[tid].depends.intersection(recursivetasks):
900 deps.update(self.runtaskentries[dep].depends.difference(self.runtaskentries[tid].depends))
901 for newtid in deps:
902 for taskname in tasknames:
903 if not newtid.endswith(":" + taskname):
904 continue
905 if newtid in self.runtaskentries:
906 extradeps += 1
907 self.runtaskentries[tid].depends.add(newtid)
908
909 bb.debug(1, "Added %s recursive dependencies in this loop" % extradeps)
910
911 # Remove recrdeptask circular references so that do_a[recrdeptask] = "do_a do_b" can work
912 for tid in recursivetasksselfref:
913 self.runtaskentries[tid].depends.difference_update(recursivetasksselfref)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600914
915 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600916 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600917
918 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500919
920 # Step B - Mark all active tasks
921 #
922 # Start with the tasks we were asked to run and mark all dependencies
923 # as active too. If the task is to be 'forced', clear its stamp. Once
924 # all active tasks are marked, prune the ones we don't need.
925
926 logger.verbose("Marking Active Tasks")
927
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600928 def mark_active(tid, depth):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500929 """
930 Mark an item as active along with its depends
931 (calls itself recursively)
932 """
933
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600934 if tid in runq_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500935 return
936
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600937 runq_build[tid] = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500938
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600939 depends = self.runtaskentries[tid].depends
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500940 for depend in depends:
941 mark_active(depend, depth+1)
942
Brad Bishop79641f22019-09-10 07:20:22 -0400943 def invalidate_task(tid, error_nostamp):
944 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
945 taskdep = self.dataCaches[mc].task_deps[taskfn]
946 if fn + ":" + taskname not in taskData[mc].taskentries:
947 logger.warning("Task %s does not exist, invalidating this task will have no effect" % taskname)
948 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
949 if error_nostamp:
950 bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
951 else:
952 bb.debug(1, "Task %s is marked nostamp, cannot invalidate this task" % taskname)
953 else:
954 logger.verbose("Invalidate task %s, %s", taskname, fn)
Andrew Geissler517393d2023-01-13 08:55:19 -0600955 bb.parse.siggen.invalidate_task(taskname, taskfn)
Brad Bishop79641f22019-09-10 07:20:22 -0400956
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600957 self.target_tids = []
958 for (mc, target, task, fn) in self.targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500959
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600960 if target not in taskData[mc].build_targets or not taskData[mc].build_targets[target]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500961 continue
962
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600963 if target in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500964 continue
965
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500966 parents = False
967 if task.endswith('-'):
968 parents = True
969 task = task[:-1]
970
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600971 if fn in taskData[mc].failed_fns:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500972 continue
973
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600974 # fn already has mc prefix
975 tid = fn + ":" + task
976 self.target_tids.append(tid)
977 if tid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500978 import difflib
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600979 tasks = []
980 for x in taskData[mc].taskentries:
981 if x.startswith(fn + ":"):
982 tasks.append(taskname_from_tid(x))
983 close_matches = difflib.get_close_matches(task, tasks, cutoff=0.7)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500984 if close_matches:
985 extra = ". Close matches:\n %s" % "\n ".join(close_matches)
986 else:
987 extra = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600988 bb.msg.fatal("RunQueue", "Task %s does not exist for target %s (%s)%s" % (task, target, tid, extra))
989
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500990 # For tasks called "XXXX-", ony run their dependencies
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500991 if parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600992 for i in self.runtaskentries[tid].depends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500993 mark_active(i, 1)
994 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600995 mark_active(tid, 1)
996
997 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600998 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500999
1000 # Step C - Prune all inactive tasks
1001 #
1002 # Once all active tasks are marked, prune the ones we don't need.
1003
Brad Bishop316dfdd2018-06-25 12:45:53 -04001004 # Handle --runall
1005 if self.cooker.configuration.runall:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001006 # re-run the mark_active and then drop unused tasks from new list
Andrew Geissler595f6302022-01-24 19:11:47 +00001007 reduced_tasklist = set(self.runtaskentries.keys())
1008 for tid in list(self.runtaskentries.keys()):
1009 if tid not in runq_build:
1010 reduced_tasklist.remove(tid)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001011 runq_build = {}
Brad Bishop316dfdd2018-06-25 12:45:53 -04001012
1013 for task in self.cooker.configuration.runall:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001014 if not task.startswith("do_"):
1015 task = "do_{0}".format(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001016 runall_tids = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00001017 for tid in reduced_tasklist:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001018 wanttid = "{0}:{1}".format(fn_from_tid(tid), task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001019 if wanttid in self.runtaskentries:
1020 runall_tids.add(wanttid)
1021
1022 for tid in list(runall_tids):
Andrew Geissler595f6302022-01-24 19:11:47 +00001023 mark_active(tid, 1)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001024 self.target_tids.append(tid)
Brad Bishop79641f22019-09-10 07:20:22 -04001025 if self.cooker.configuration.force:
1026 invalidate_task(tid, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001027
Andrew Geissler595f6302022-01-24 19:11:47 +00001028 delcount = set()
1029 for tid in list(self.runtaskentries.keys()):
1030 if tid not in runq_build:
1031 delcount.add(tid)
1032 del self.runtaskentries[tid]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001033
Andrew Geissler595f6302022-01-24 19:11:47 +00001034 if self.cooker.configuration.runall:
1035 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001036 bb.msg.fatal("RunQueue", "Could not find any tasks with the tasknames %s to run within the recipes of the taskgraphs of the targets %s" % (str(self.cooker.configuration.runall), str(self.targets)))
1037
1038 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001039 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001040
1041 # Handle runonly
1042 if self.cooker.configuration.runonly:
1043 # re-run the mark_active and then drop unused tasks from new list
1044 runq_build = {}
1045
1046 for task in self.cooker.configuration.runonly:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001047 if not task.startswith("do_"):
1048 task = "do_{0}".format(task)
Andrew Geissler595f6302022-01-24 19:11:47 +00001049 runonly_tids = [k for k in self.runtaskentries.keys() if taskname_from_tid(k) == task]
Brad Bishop316dfdd2018-06-25 12:45:53 -04001050
Andrew Geissler595f6302022-01-24 19:11:47 +00001051 for tid in runonly_tids:
1052 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001053 if self.cooker.configuration.force:
1054 invalidate_task(tid, False)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001055
1056 for tid in list(self.runtaskentries.keys()):
1057 if tid not in runq_build:
Andrew Geissler595f6302022-01-24 19:11:47 +00001058 delcount.add(tid)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001059 del self.runtaskentries[tid]
1060
Andrew Geissler595f6302022-01-24 19:11:47 +00001061 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001062 bb.msg.fatal("RunQueue", "Could not find any tasks with the tasknames %s to run within the taskgraphs of the targets %s" % (str(self.cooker.configuration.runonly), str(self.targets)))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001063
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001064 #
1065 # Step D - Sanity checks and computation
1066 #
1067
1068 # Check to make sure we still have tasks to run
Andrew Geissler595f6302022-01-24 19:11:47 +00001069 if not self.runtaskentries:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001070 if not taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001071 bb.msg.fatal("RunQueue", "All buildable tasks have been run but the build is incomplete (--continue mode). Errors for the tasks that failed will have been printed above.")
1072 else:
1073 bb.msg.fatal("RunQueue", "No active tasks and not in --continue mode?! Please report this bug.")
1074
Brad Bishop316dfdd2018-06-25 12:45:53 -04001075 logger.verbose("Pruned %s inactive tasks, %s left", len(delcount), len(self.runtaskentries))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001076
1077 logger.verbose("Assign Weightings")
1078
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001079 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001080 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001081
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001082 # Generate a list of reverse dependencies to ease future calculations
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001083 for tid in self.runtaskentries:
1084 for dep in self.runtaskentries[tid].depends:
1085 self.runtaskentries[dep].revdeps.add(tid)
1086
1087 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001088 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001089
1090 # Identify tasks at the end of dependency chains
1091 # Error on circular dependency loops (length two)
1092 endpoints = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001093 for tid in self.runtaskentries:
1094 revdeps = self.runtaskentries[tid].revdeps
Andrew Geissler595f6302022-01-24 19:11:47 +00001095 if not revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001096 endpoints.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001097 for dep in revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001098 if dep in self.runtaskentries[tid].depends:
1099 bb.msg.fatal("RunQueue", "Task %s has circular dependency on %s" % (tid, dep))
1100
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001101
1102 logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
1103
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001104 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001105 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001106
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001107 # Calculate task weights
1108 # Check of higher length circular dependencies
1109 self.runq_weight = self.calculate_task_weights(endpoints)
1110
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001111 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001112 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001113
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001114 # Sanity Check - Check for multiple tasks building the same provider
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001115 for mc in self.dataCaches:
1116 prov_list = {}
1117 seen_fn = []
1118 for tid in self.runtaskentries:
1119 (tidmc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1120 if taskfn in seen_fn:
1121 continue
1122 if mc != tidmc:
1123 continue
1124 seen_fn.append(taskfn)
1125 for prov in self.dataCaches[mc].fn_provides[taskfn]:
1126 if prov not in prov_list:
1127 prov_list[prov] = [taskfn]
1128 elif taskfn not in prov_list[prov]:
1129 prov_list[prov].append(taskfn)
1130 for prov in prov_list:
1131 if len(prov_list[prov]) < 2:
1132 continue
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001133 if prov in self.multi_provider_allowed:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001134 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001135 seen_pn = []
1136 # If two versions of the same PN are being built its fatal, we don't support it.
1137 for fn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001138 pn = self.dataCaches[mc].pkg_fn[fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001139 if pn not in seen_pn:
1140 seen_pn.append(pn)
1141 else:
1142 bb.fatal("Multiple versions of %s are due to be built (%s). Only one version of a given PN should be built in any given build. You likely need to set PREFERRED_VERSION_%s to select the correct version or don't depend on multiple versions." % (pn, " ".join(prov_list[prov]), pn))
Andrew Geissler595f6302022-01-24 19:11:47 +00001143 msgs = ["Multiple .bb files are due to be built which each provide %s:\n %s" % (prov, "\n ".join(prov_list[prov]))]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001144 #
1145 # Construct a list of things which uniquely depend on each provider
1146 # since this may help the user figure out which dependency is triggering this warning
1147 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001148 msgs.append("\nA list of tasks depending on these providers is shown and may help explain where the dependency comes from.")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001149 deplist = {}
1150 commondeps = None
1151 for provfn in prov_list[prov]:
1152 deps = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001153 for tid in self.runtaskentries:
1154 fn = fn_from_tid(tid)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001155 if fn != provfn:
1156 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001157 for dep in self.runtaskentries[tid].revdeps:
1158 fn = fn_from_tid(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001159 if fn == provfn:
1160 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001161 deps.add(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001162 if not commondeps:
1163 commondeps = set(deps)
1164 else:
1165 commondeps &= deps
1166 deplist[provfn] = deps
1167 for provfn in deplist:
Andrew Geissler595f6302022-01-24 19:11:47 +00001168 msgs.append("\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001169 #
1170 # Construct a list of provides and runtime providers for each recipe
1171 # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC)
1172 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001173 msgs.append("\nIt could be that one recipe provides something the other doesn't and should. The following provider and runtime provider differences may be helpful.")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001174 provide_results = {}
1175 rprovide_results = {}
1176 commonprovs = None
1177 commonrprovs = None
1178 for provfn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001179 provides = set(self.dataCaches[mc].fn_provides[provfn])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001180 rprovides = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001181 for rprovide in self.dataCaches[mc].rproviders:
1182 if provfn in self.dataCaches[mc].rproviders[rprovide]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001183 rprovides.add(rprovide)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001184 for package in self.dataCaches[mc].packages:
1185 if provfn in self.dataCaches[mc].packages[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001186 rprovides.add(package)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001187 for package in self.dataCaches[mc].packages_dynamic:
1188 if provfn in self.dataCaches[mc].packages_dynamic[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001189 rprovides.add(package)
1190 if not commonprovs:
1191 commonprovs = set(provides)
1192 else:
1193 commonprovs &= provides
1194 provide_results[provfn] = provides
1195 if not commonrprovs:
1196 commonrprovs = set(rprovides)
1197 else:
1198 commonrprovs &= rprovides
1199 rprovide_results[provfn] = rprovides
Andrew Geissler595f6302022-01-24 19:11:47 +00001200 #msgs.append("\nCommon provides:\n %s" % ("\n ".join(commonprovs)))
1201 #msgs.append("\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001202 for provfn in prov_list[prov]:
Andrew Geissler595f6302022-01-24 19:11:47 +00001203 msgs.append("\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs)))
1204 msgs.append("\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001205
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001206 if self.warn_multi_bb:
Andrew Geissler595f6302022-01-24 19:11:47 +00001207 logger.verbnote("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001208 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00001209 logger.error("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001210
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001211 self.init_progress_reporter.next_stage()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001212 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001213 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001214
1215 # Iterate over the task list looking for tasks with a 'setscene' function
Andrew Geissler82c905d2020-04-13 13:39:40 -05001216 self.runq_setscene_tids = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001217 if not self.cooker.configuration.nosetscene:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001218 for tid in self.runtaskentries:
1219 (mc, fn, taskname, _) = split_tid_mcfn(tid)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001220 setscenetid = tid + "_setscene"
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001221 if setscenetid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001222 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -05001223 self.runq_setscene_tids.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001224
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001225 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001226 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001227
1228 # Invalidate task if force mode active
1229 if self.cooker.configuration.force:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001230 for tid in self.target_tids:
1231 invalidate_task(tid, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001232
1233 # Invalidate task if invalidate mode active
1234 if self.cooker.configuration.invalidate_stamp:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001235 for tid in self.target_tids:
1236 fn = fn_from_tid(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001237 for st in self.cooker.configuration.invalidate_stamp.split(','):
1238 if not st.startswith("do_"):
1239 st = "do_%s" % st
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001240 invalidate_task(fn + ":" + st, True)
1241
1242 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001243 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001244
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001245 # Create and print to the logs a virtual/xxxx -> PN (fn) table
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001246 for mc in taskData:
1247 virtmap = taskData[mc].get_providermap(prefix="virtual/")
1248 virtpnmap = {}
1249 for v in virtmap:
1250 virtpnmap[v] = self.dataCaches[mc].pkg_fn[virtmap[v]]
1251 bb.debug(2, "%s resolved to: %s (%s)" % (v, virtpnmap[v], virtmap[v]))
1252 if hasattr(bb.parse.siggen, "tasks_resolved"):
1253 bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
1254
1255 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001256 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001257
Brad Bishop00e122a2019-10-05 11:10:57 -04001258 bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
1259
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001260 # Iterate over the task list and call into the siggen code
1261 dealtwith = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001262 todeal = set(self.runtaskentries)
Andrew Geissler595f6302022-01-24 19:11:47 +00001263 while todeal:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001264 for tid in todeal.copy():
Andrew Geissler595f6302022-01-24 19:11:47 +00001265 if not (self.runtaskentries[tid].depends - dealtwith):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001266 dealtwith.add(tid)
1267 todeal.remove(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001268 self.prepare_task_hash(tid)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001269 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001270
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001271 bb.parse.siggen.writeout_file_checksum_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001272
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001273 #self.dump_data()
1274 return len(self.runtaskentries)
1275
Brad Bishop19323692019-04-05 15:28:33 -04001276 def prepare_task_hash(self, tid):
Andrew Geissler517393d2023-01-13 08:55:19 -06001277 bb.parse.siggen.prep_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
1278 self.runtaskentries[tid].hash = bb.parse.siggen.get_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
Brad Bishop08902b02019-08-20 09:16:51 -04001279 self.runtaskentries[tid].unihash = bb.parse.siggen.get_unihash(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001280
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001281 def dump_data(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001282 """
1283 Dump some debug information on the internal data structures
1284 """
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001285 logger.debug3("run_tasks:")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001286 for tid in self.runtaskentries:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001287 logger.debug3(" %s: %s Deps %s RevDeps %s", tid,
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001288 self.runtaskentries[tid].weight,
1289 self.runtaskentries[tid].depends,
1290 self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001291
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001292class RunQueueWorker():
1293 def __init__(self, process, pipe):
1294 self.process = process
1295 self.pipe = pipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001296
1297class RunQueue:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001298 def __init__(self, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001299
1300 self.cooker = cooker
1301 self.cfgData = cfgData
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001302 self.rqdata = RunQueueData(self, cooker, cfgData, dataCaches, taskData, targets)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001303
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001304 self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION") or None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001305 self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID") or None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001306
1307 self.state = runQueuePrepare
1308
1309 # For disk space monitor
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001310 # Invoked at regular time intervals via the bitbake heartbeat event
1311 # while the build is running. We generate a unique name for the handler
1312 # here, just in case that there ever is more than one RunQueue instance,
Brad Bishop96ff1982019-08-19 13:50:42 -04001313 # start the handler when reaching runQueueSceneInit, and stop it when
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001314 # done with the build.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001315 self.dm = monitordisk.diskMonitor(cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001316 self.dm_event_handler_name = '_bb_diskmonitor_' + str(id(self))
1317 self.dm_event_handler_registered = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001318 self.rqexe = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001319 self.worker = {}
1320 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001321
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001322 @staticmethod
1323 def send_pickled_data(worker, data, name):
1324 msg = bytearray()
1325 msg.extend(b"<" + name.encode() + b">")
1326 pickled_data = pickle.dumps(data)
1327 msg.extend(len(pickled_data).to_bytes(4, 'big'))
1328 msg.extend(pickled_data)
1329 msg.extend(b"</" + name.encode() + b">")
1330 worker.stdin.write(msg)
1331
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001332 def _start_worker(self, mc, fakeroot = False, rqexec = None):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001333 logger.debug("Starting bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334 magic = "decafbad"
1335 if self.cooker.configuration.profile:
1336 magic = "decafbadbad"
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001337 fakerootlogs = None
Andrew Geissler220dafd2023-10-04 10:18:08 -05001338
1339 workerscript = os.path.realpath(os.path.dirname(__file__) + "/../../bin/bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001340 if fakeroot:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001341 magic = magic + "beef"
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001342 mcdata = self.cooker.databuilder.mcdata[mc]
Brad Bishop19323692019-04-05 15:28:33 -04001343 fakerootcmd = shlex.split(mcdata.getVar("FAKEROOTCMD"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001344 fakerootenv = (mcdata.getVar("FAKEROOTBASEENV") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001345 env = os.environ.copy()
1346 for key, value in (var.split('=') for var in fakerootenv):
1347 env[key] = value
Andrew Geissler220dafd2023-10-04 10:18:08 -05001348 worker = subprocess.Popen(fakerootcmd + [sys.executable, workerscript, magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001349 fakerootlogs = self.rqdata.dataCaches[mc].fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001350 else:
Andrew Geissler220dafd2023-10-04 10:18:08 -05001351 worker = subprocess.Popen([sys.executable, workerscript, magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001352 bb.utils.nonblockingfd(worker.stdout)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001353 workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, self, rqexec, fakerootlogs=fakerootlogs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001354
1355 workerdata = {
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001356 "sigdata" : bb.parse.siggen.get_taskdata(),
Andrew Geissler82c905d2020-04-13 13:39:40 -05001357 "logdefaultlevel" : bb.msg.loggerDefaultLogLevel,
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001358 "build_verbose_shell" : self.cooker.configuration.build_verbose_shell,
1359 "build_verbose_stdout" : self.cooker.configuration.build_verbose_stdout,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001360 "logdefaultdomain" : bb.msg.loggerDefaultDomains,
1361 "prhost" : self.cooker.prhost,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001362 "buildname" : self.cfgData.getVar("BUILDNAME"),
1363 "date" : self.cfgData.getVar("DATE"),
1364 "time" : self.cfgData.getVar("TIME"),
Brad Bishopa34c0302019-09-23 22:34:48 -04001365 "hashservaddr" : self.cooker.hashservaddr,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001366 "umask" : self.cfgData.getVar("BB_DEFAULT_UMASK"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001367 }
1368
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001369 RunQueue.send_pickled_data(worker, self.cooker.configuration, "cookerconfig")
1370 RunQueue.send_pickled_data(worker, self.cooker.extraconfigdata, "extraconfigdata")
1371 RunQueue.send_pickled_data(worker, workerdata, "workerdata")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001372 worker.stdin.flush()
1373
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001374 return RunQueueWorker(worker, workerpipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001375
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001376 def _teardown_worker(self, worker):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001377 if not worker:
1378 return
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001379 logger.debug("Teardown for bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001380 try:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001381 RunQueue.send_pickled_data(worker.process, b"", "quit")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001382 worker.process.stdin.flush()
1383 worker.process.stdin.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001384 except IOError:
1385 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001386 while worker.process.returncode is None:
1387 worker.pipe.read()
1388 worker.process.poll()
1389 while worker.pipe.read():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001390 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001391 worker.pipe.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001392
Patrick Williams169d7bc2024-01-05 11:33:25 -06001393 def start_worker(self, rqexec):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001394 if self.worker:
1395 self.teardown_workers()
1396 self.teardown = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001397 for mc in self.rqdata.dataCaches:
Patrick Williams169d7bc2024-01-05 11:33:25 -06001398 self.worker[mc] = self._start_worker(mc, False, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001399
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001400 def start_fakeworker(self, rqexec, mc):
1401 if not mc in self.fakeworker:
1402 self.fakeworker[mc] = self._start_worker(mc, True, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001403
1404 def teardown_workers(self):
1405 self.teardown = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001406 for mc in self.worker:
1407 self._teardown_worker(self.worker[mc])
1408 self.worker = {}
1409 for mc in self.fakeworker:
1410 self._teardown_worker(self.fakeworker[mc])
1411 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001412
1413 def read_workers(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001414 for mc in self.worker:
1415 self.worker[mc].pipe.read()
1416 for mc in self.fakeworker:
1417 self.fakeworker[mc].pipe.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001418
1419 def active_fds(self):
1420 fds = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001421 for mc in self.worker:
1422 fds.append(self.worker[mc].pipe.input)
1423 for mc in self.fakeworker:
1424 fds.append(self.fakeworker[mc].pipe.input)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001425 return fds
1426
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001427 def check_stamp_task(self, tid, taskname = None, recurse = False, cache = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001428 def get_timestamp(f):
1429 try:
1430 if not os.access(f, os.F_OK):
1431 return None
1432 return os.stat(f)[stat.ST_MTIME]
1433 except:
1434 return None
1435
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001436 (mc, fn, tn, taskfn) = split_tid_mcfn(tid)
1437 if taskname is None:
1438 taskname = tn
1439
Andrew Geissler517393d2023-01-13 08:55:19 -06001440 stampfile = bb.parse.siggen.stampfile_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001441
1442 # If the stamp is missing, it's not current
1443 if not os.access(stampfile, os.F_OK):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001444 logger.debug2("Stampfile %s not available", stampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001445 return False
1446 # If it's a 'nostamp' task, it's not current
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001447 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001448 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001449 logger.debug2("%s.%s is nostamp\n", fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001450 return False
1451
Andrew Geissler517393d2023-01-13 08:55:19 -06001452 if taskname.endswith("_setscene"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001453 return True
1454
1455 if cache is None:
1456 cache = {}
1457
1458 iscurrent = True
1459 t1 = get_timestamp(stampfile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001460 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001461 if iscurrent:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001462 (mc2, fn2, taskname2, taskfn2) = split_tid_mcfn(dep)
Andrew Geissler517393d2023-01-13 08:55:19 -06001463 stampfile2 = bb.parse.siggen.stampfile_mcfn(taskname2, taskfn2)
1464 stampfile3 = bb.parse.siggen.stampfile_mcfn(taskname2 + "_setscene", taskfn2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001465 t2 = get_timestamp(stampfile2)
1466 t3 = get_timestamp(stampfile3)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001467 if t3 and not t2:
1468 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001469 if t3 and t3 > t2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001470 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00001471 if fn == fn2:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001472 if not t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001473 logger.debug2('Stampfile %s does not exist', stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001474 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001475 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001476 if t1 < t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001477 logger.debug2('Stampfile %s < %s', stampfile, stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001478 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001479 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001480 if recurse and iscurrent:
1481 if dep in cache:
1482 iscurrent = cache[dep]
1483 if not iscurrent:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001484 logger.debug2('Stampfile for dependency %s:%s invalid (cached)' % (fn2, taskname2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001485 else:
1486 iscurrent = self.check_stamp_task(dep, recurse=True, cache=cache)
1487 cache[dep] = iscurrent
1488 if recurse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001489 cache[tid] = iscurrent
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001490 return iscurrent
1491
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001492 def validate_hashes(self, tocheck, data, currentcount=0, siginfo=False, summary=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04001493 valid = set()
1494 if self.hashvalidate:
Brad Bishop08902b02019-08-20 09:16:51 -04001495 sq_data = {}
1496 sq_data['hash'] = {}
1497 sq_data['hashfn'] = {}
1498 sq_data['unihash'] = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04001499 for tid in tocheck:
1500 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04001501 sq_data['hash'][tid] = self.rqdata.runtaskentries[tid].hash
1502 sq_data['hashfn'][tid] = self.rqdata.dataCaches[mc].hashfn[taskfn]
1503 sq_data['unihash'][tid] = self.rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -04001504
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001505 valid = self.validate_hash(sq_data, data, siginfo, currentcount, summary)
Brad Bishop96ff1982019-08-19 13:50:42 -04001506
1507 return valid
1508
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001509 def validate_hash(self, sq_data, d, siginfo, currentcount, summary):
1510 locs = {"sq_data" : sq_data, "d" : d, "siginfo" : siginfo, "currentcount" : currentcount, "summary" : summary}
Brad Bishop19323692019-04-05 15:28:33 -04001511
Brad Bishop08902b02019-08-20 09:16:51 -04001512 # Metadata has **kwargs so args can be added, sq_data can also gain new fields
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001513 call = self.hashvalidate + "(sq_data, d, siginfo=siginfo, currentcount=currentcount, summary=summary)"
Brad Bishop19323692019-04-05 15:28:33 -04001514
Brad Bishop19323692019-04-05 15:28:33 -04001515 return bb.utils.better_eval(call, locs)
1516
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001517 def _execute_runqueue(self):
1518 """
1519 Run the tasks in a queue prepared by rqdata.prepare()
1520 Upon failure, optionally try to recover the build using any alternate providers
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001521 (if the halt on failure configuration option isn't set)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001522 """
1523
1524 retval = True
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001525 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001526
1527 if self.state is runQueuePrepare:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001528 # NOTE: if you add, remove or significantly refactor the stages of this
1529 # process then you should recalculate the weightings here. This is quite
1530 # easy to do - just change the next line temporarily to pass debug=True as
1531 # the last parameter and you'll get a printout of the weightings as well
1532 # as a map to the lines where next_stage() was called. Of course this isn't
1533 # critical, but it helps to keep the progress reporting accurate.
1534 self.rqdata.init_progress_reporter = bb.progress.MultiStageProcessProgressReporter(self.cooker.data,
1535 "Initialising tasks",
1536 [43, 967, 4, 3, 1, 5, 3, 7, 13, 1, 2, 1, 1, 246, 35, 1, 38, 1, 35, 2, 338, 204, 142, 3, 3, 37, 244])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001537 if self.rqdata.prepare() == 0:
1538 self.state = runQueueComplete
1539 else:
1540 self.state = runQueueSceneInit
Brad Bishop00e122a2019-10-05 11:10:57 -04001541 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001542
1543 if self.state is runQueueSceneInit:
Brad Bishop96ff1982019-08-19 13:50:42 -04001544 self.rqdata.init_progress_reporter.next_stage()
1545
1546 # we are ready to run, emit dependency info to any UI or class which
1547 # needs it
1548 depgraph = self.cooker.buildDependTree(self, self.rqdata.taskData)
1549 self.rqdata.init_progress_reporter.next_stage()
1550 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
1551
Brad Bishope2d5b612018-11-23 10:55:50 +13001552 if not self.dm_event_handler_registered:
1553 res = bb.event.register(self.dm_event_handler_name,
Andrew Geissler517393d2023-01-13 08:55:19 -06001554 lambda x, y: self.dm.check(self) if self.state in [runQueueRunning, runQueueCleanUp] else False,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001555 ('bb.event.HeartbeatEvent',), data=self.cfgData)
Brad Bishope2d5b612018-11-23 10:55:50 +13001556 self.dm_event_handler_registered = True
1557
Patrick Williams169d7bc2024-01-05 11:33:25 -06001558 self.rqdata.init_progress_reporter.next_stage()
1559 self.rqexe = RunQueueExecute(self)
1560
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001561 dump = self.cooker.configuration.dump_signatures
1562 if dump:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001563 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001564 if 'printdiff' in dump:
1565 invalidtasks = self.print_diffscenetasks()
1566 self.dump_signatures(dump)
1567 if 'printdiff' in dump:
1568 self.write_diffscenetasks(invalidtasks)
1569 self.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001570
Brad Bishop96ff1982019-08-19 13:50:42 -04001571 if self.state is runQueueSceneInit:
Patrick Williams169d7bc2024-01-05 11:33:25 -06001572 self.start_worker(self.rqexe)
1573 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001574
Brad Bishop96ff1982019-08-19 13:50:42 -04001575 # If we don't have any setscene functions, skip execution
Andrew Geissler595f6302022-01-24 19:11:47 +00001576 if not self.rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04001577 logger.info('No setscene tasks')
1578 for tid in self.rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00001579 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04001580 self.rqexe.setbuildable(tid)
1581 self.rqexe.tasks_notcovered.add(tid)
1582 self.rqexe.sqdone = True
1583 logger.info('Executing Tasks')
1584 self.state = runQueueRunning
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001585
1586 if self.state is runQueueRunning:
1587 retval = self.rqexe.execute()
1588
1589 if self.state is runQueueCleanUp:
1590 retval = self.rqexe.finish()
1591
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001592 build_done = self.state is runQueueComplete or self.state is runQueueFailed
1593
1594 if build_done and self.dm_event_handler_registered:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001595 bb.event.remove(self.dm_event_handler_name, None, data=self.cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001596 self.dm_event_handler_registered = False
1597
1598 if build_done and self.rqexe:
Brad Bishop08902b02019-08-20 09:16:51 -04001599 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001600 self.teardown_workers()
Brad Bishop96ff1982019-08-19 13:50:42 -04001601 if self.rqexe:
1602 if self.rqexe.stats.failed:
1603 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)
1604 else:
1605 # Let's avoid the word "failed" if nothing actually did
1606 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 -05001607
1608 if self.state is runQueueFailed:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001609 raise bb.runqueue.TaskFailure(self.rqexe.failed_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001610
1611 if self.state is runQueueComplete:
1612 # All done
1613 return False
1614
1615 # Loop
1616 return retval
1617
1618 def execute_runqueue(self):
1619 # Catch unexpected exceptions and ensure we exit when an error occurs, not loop.
1620 try:
1621 return self._execute_runqueue()
1622 except bb.runqueue.TaskFailure:
1623 raise
1624 except SystemExit:
1625 raise
1626 except bb.BBHandledException:
1627 try:
1628 self.teardown_workers()
1629 except:
1630 pass
1631 self.state = runQueueComplete
1632 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001633 except Exception as err:
1634 logger.exception("An uncaught exception occurred in runqueue")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001635 try:
1636 self.teardown_workers()
1637 except:
1638 pass
1639 self.state = runQueueComplete
1640 raise
1641
1642 def finish_runqueue(self, now = False):
1643 if not self.rqexe:
1644 self.state = runQueueComplete
1645 return
1646
1647 if now:
1648 self.rqexe.finish_now()
1649 else:
1650 self.rqexe.finish()
1651
Andrew Geissler517393d2023-01-13 08:55:19 -06001652 def _rq_dump_sigtid(self, tids):
1653 for tid in tids:
1654 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1655 dataCaches = self.rqdata.dataCaches
1656 bb.parse.siggen.dump_sigtask(taskfn, taskname, dataCaches[mc].stamp[taskfn], True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001657
1658 def dump_signatures(self, options):
Andrew Geissler517393d2023-01-13 08:55:19 -06001659 if bb.cooker.CookerFeatures.RECIPE_SIGGEN_INFO not in self.cooker.featureset:
1660 bb.fatal("The dump signatures functionality needs the RECIPE_SIGGEN_INFO feature enabled")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001661
Andrew Geissler517393d2023-01-13 08:55:19 -06001662 bb.note("Writing task signature files")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001663
1664 max_process = int(self.cfgData.getVar("BB_NUMBER_PARSE_THREADS") or os.cpu_count() or 1)
Andrew Geissler517393d2023-01-13 08:55:19 -06001665 def chunkify(l, n):
1666 return [l[i::n] for i in range(n)]
1667 tids = chunkify(list(self.rqdata.runtaskentries), max_process)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001668 # We cannot use the real multiprocessing.Pool easily due to some local data
1669 # that can't be pickled. This is a cheap multi-process solution.
1670 launched = []
Andrew Geissler517393d2023-01-13 08:55:19 -06001671 while tids:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001672 if len(launched) < max_process:
Andrew Geissler517393d2023-01-13 08:55:19 -06001673 p = Process(target=self._rq_dump_sigtid, args=(tids.pop(), ))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001674 p.start()
1675 launched.append(p)
1676 for q in launched:
1677 # The finished processes are joined when calling is_alive()
1678 if not q.is_alive():
1679 launched.remove(q)
1680 for p in launched:
1681 p.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001682
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001683 bb.parse.siggen.dump_sigs(self.rqdata.dataCaches, options)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001684
1685 return
1686
1687 def print_diffscenetasks(self):
1688
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001689 noexec = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001690 tocheck = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001691
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001692 for tid in self.rqdata.runtaskentries:
1693 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1694 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001695
1696 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001697 noexec.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001698 continue
1699
Brad Bishop96ff1982019-08-19 13:50:42 -04001700 tocheck.add(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001701
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001702 valid_new = self.validate_hashes(tocheck, self.cooker.data, 0, True, summary=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001703
1704 # Tasks which are both setscene and noexec never care about dependencies
1705 # We therefore find tasks which are setscene and noexec and mark their
1706 # unique dependencies as valid.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001707 for tid in noexec:
1708 if tid not in self.rqdata.runq_setscene_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001709 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001710 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001711 hasnoexecparents = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001712 for dep2 in self.rqdata.runtaskentries[dep].revdeps:
1713 if dep2 in self.rqdata.runq_setscene_tids and dep2 in noexec:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001714 continue
1715 hasnoexecparents = False
1716 break
1717 if hasnoexecparents:
1718 valid_new.add(dep)
1719
1720 invalidtasks = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001721 for tid in self.rqdata.runtaskentries:
1722 if tid not in valid_new and tid not in noexec:
1723 invalidtasks.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001724
1725 found = set()
1726 processed = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001727 for tid in invalidtasks:
1728 toprocess = set([tid])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001729 while toprocess:
1730 next = set()
1731 for t in toprocess:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001732 for dep in self.rqdata.runtaskentries[t].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001733 if dep in invalidtasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001734 found.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001735 if dep not in processed:
1736 processed.add(dep)
1737 next.add(dep)
1738 toprocess = next
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001739 if tid in found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001740 toprocess = set()
1741
1742 tasklist = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001743 for tid in invalidtasks.difference(found):
1744 tasklist.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001745
1746 if tasklist:
1747 bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
1748
1749 return invalidtasks.difference(found)
1750
1751 def write_diffscenetasks(self, invalidtasks):
Patrick Williams169d7bc2024-01-05 11:33:25 -06001752 bb.siggen.check_siggen_version(bb.siggen)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001753
1754 # Define recursion callback
1755 def recursecb(key, hash1, hash2):
1756 hashes = [hash1, hash2]
Patrick Williams169d7bc2024-01-05 11:33:25 -06001757 bb.debug(1, "Recursively looking for recipe {} hashes {}".format(key, hashes))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001758 hashfiles = bb.siggen.find_siginfo(key, None, hashes, self.cfgData)
Patrick Williams169d7bc2024-01-05 11:33:25 -06001759 bb.debug(1, "Found hashfiles:\n{}".format(hashfiles))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001760
1761 recout = []
1762 if len(hashfiles) == 2:
Patrick Williams169d7bc2024-01-05 11:33:25 -06001763 out2 = bb.siggen.compare_sigfiles(hashfiles[hash1]['path'], hashfiles[hash2]['path'], recursecb)
Brad Bishopc342db32019-05-15 21:57:59 -04001764 recout.extend(list(' ' + l for l in out2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001765 else:
1766 recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
1767
1768 return recout
1769
1770
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001771 for tid in invalidtasks:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001772 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1773 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001774 h = self.rqdata.runtaskentries[tid].unihash
Patrick Williams169d7bc2024-01-05 11:33:25 -06001775 bb.debug(1, "Looking for recipe {} task {}".format(pn, taskname))
Patrick Williams03907ee2022-05-01 06:28:52 -05001776 matches = bb.siggen.find_siginfo(pn, taskname, [], self.cooker.databuilder.mcdata[mc])
Patrick Williams169d7bc2024-01-05 11:33:25 -06001777 bb.debug(1, "Found hashfiles:\n{}".format(matches))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001778 match = None
Patrick Williams169d7bc2024-01-05 11:33:25 -06001779 for m in matches.values():
1780 if h in m['path']:
1781 match = m['path']
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001782 if match is None:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001783 bb.fatal("Can't find a task we're supposed to have written out? (hash: %s tid: %s)?" % (h, tid))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001784 matches = {k : v for k, v in iter(matches.items()) if h not in k}
Patrick Williams169d7bc2024-01-05 11:33:25 -06001785 matches_local = {k : v for k, v in iter(matches.items()) if h not in k and not v['sstate']}
1786 if matches_local:
1787 matches = matches_local
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001788 if matches:
Patrick Williams169d7bc2024-01-05 11:33:25 -06001789 latestmatch = matches[sorted(matches.keys(), key=lambda h: matches[h]['time'])[-1]]['path']
Brad Bishop19323692019-04-05 15:28:33 -04001790 prevh = __find_sha256__.search(latestmatch).group(0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001791 output = bb.siggen.compare_sigfiles(latestmatch, match, recursecb)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001792 bb.plain("\nTask %s:%s couldn't be used from the cache because:\n We need hash %s, most recent matching task was %s\n " % (pn, taskname, h, prevh) + '\n '.join(output))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001793
Brad Bishop96ff1982019-08-19 13:50:42 -04001794
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001795class RunQueueExecute:
1796
1797 def __init__(self, rq):
1798 self.rq = rq
1799 self.cooker = rq.cooker
1800 self.cfgData = rq.cfgData
1801 self.rqdata = rq.rqdata
1802
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001803 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
1804 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001805 self.max_cpu_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_CPU")
1806 self.max_io_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_IO")
Patrick Williams92b42cb2022-09-03 06:53:57 -05001807 self.max_memory_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_MEMORY")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001808
Brad Bishop96ff1982019-08-19 13:50:42 -04001809 self.sq_buildable = set()
1810 self.sq_running = set()
1811 self.sq_live = set()
1812
Brad Bishop08902b02019-08-20 09:16:51 -04001813 self.updated_taskhash_queue = []
1814 self.pending_migrations = set()
1815
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001816 self.runq_buildable = set()
1817 self.runq_running = set()
1818 self.runq_complete = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001819 self.runq_tasksrun = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001820
1821 self.build_stamps = {}
1822 self.build_stamps2 = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001823 self.failed_tids = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001824 self.sq_deferred = {}
Patrick Williams169d7bc2024-01-05 11:33:25 -06001825 self.sq_needed_harddeps = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001826
1827 self.stampcache = {}
1828
Brad Bishop08902b02019-08-20 09:16:51 -04001829 self.holdoff_tasks = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04001830 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04001831 self.sqdone = False
1832
Andrew Geissler5199d832021-09-24 16:47:35 -05001833 self.stats = RunQueueStats(len(self.rqdata.runtaskentries), len(self.rqdata.runq_setscene_tids))
Brad Bishop96ff1982019-08-19 13:50:42 -04001834
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001835 if self.number_tasks <= 0:
1836 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks)
1837
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001838 lower_limit = 1.0
1839 upper_limit = 1000000.0
1840 if self.max_cpu_pressure:
1841 self.max_cpu_pressure = float(self.max_cpu_pressure)
1842 if self.max_cpu_pressure < lower_limit:
1843 bb.fatal("Invalid BB_PRESSURE_MAX_CPU %s, minimum value is %s." % (self.max_cpu_pressure, lower_limit))
1844 if self.max_cpu_pressure > upper_limit:
1845 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))
1846
1847 if self.max_io_pressure:
1848 self.max_io_pressure = float(self.max_io_pressure)
1849 if self.max_io_pressure < lower_limit:
1850 bb.fatal("Invalid BB_PRESSURE_MAX_IO %s, minimum value is %s." % (self.max_io_pressure, lower_limit))
1851 if self.max_io_pressure > upper_limit:
1852 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))
1853
Patrick Williams92b42cb2022-09-03 06:53:57 -05001854 if self.max_memory_pressure:
1855 self.max_memory_pressure = float(self.max_memory_pressure)
1856 if self.max_memory_pressure < lower_limit:
1857 bb.fatal("Invalid BB_PRESSURE_MAX_MEMORY %s, minimum value is %s." % (self.max_memory_pressure, lower_limit))
1858 if self.max_memory_pressure > upper_limit:
1859 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))
1860
Brad Bishop96ff1982019-08-19 13:50:42 -04001861 # List of setscene tasks which we've covered
1862 self.scenequeue_covered = set()
1863 # List of tasks which are covered (including setscene ones)
1864 self.tasks_covered = set()
1865 self.tasks_scenequeue_done = set()
1866 self.scenequeue_notcovered = set()
1867 self.tasks_notcovered = set()
1868 self.scenequeue_notneeded = set()
1869
Brad Bishop96ff1982019-08-19 13:50:42 -04001870 schedulers = self.get_schedulers()
1871 for scheduler in schedulers:
1872 if self.scheduler == scheduler.name:
1873 self.sched = scheduler(self, self.rqdata)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001874 logger.debug("Using runqueue scheduler '%s'", scheduler.name)
Brad Bishop96ff1982019-08-19 13:50:42 -04001875 break
1876 else:
1877 bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
1878 (self.scheduler, ", ".join(obj.name for obj in schedulers)))
1879
Andrew Geissler595f6302022-01-24 19:11:47 +00001880 #if self.rqdata.runq_setscene_tids:
Brad Bishop08902b02019-08-20 09:16:51 -04001881 self.sqdata = SQData()
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001882 build_scenequeue_data(self.sqdata, self.rqdata, self)
1883
1884 update_scenequeue_data(self.sqdata.sq_revdeps, self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=True)
1885
1886 # Compute a list of 'stale' sstate tasks where the current hash does not match the one
1887 # in any stamp files. Pass the list out to metadata as an event.
1888 found = {}
1889 for tid in self.rqdata.runq_setscene_tids:
1890 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1891 stamps = bb.build.find_stale_stamps(taskname, taskfn)
1892 if stamps:
1893 if mc not in found:
1894 found[mc] = {}
1895 found[mc][tid] = stamps
1896 for mc in found:
1897 event = bb.event.StaleSetSceneTasks(found[mc])
1898 bb.event.fire(event, self.cooker.databuilder.mcdata[mc])
Brad Bishop96ff1982019-08-19 13:50:42 -04001899
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001900 def runqueue_process_waitpid(self, task, status, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001901
1902 # self.build_stamps[pid] may not exist when use shared work directory.
1903 if task in self.build_stamps:
1904 self.build_stamps2.remove(self.build_stamps[task])
1905 del self.build_stamps[task]
1906
Brad Bishop96ff1982019-08-19 13:50:42 -04001907 if task in self.sq_live:
1908 if status != 0:
1909 self.sq_task_fail(task, status)
1910 else:
1911 self.sq_task_complete(task)
1912 self.sq_live.remove(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05001913 self.stats.updateActiveSetscene(len(self.sq_live))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001914 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04001915 if status != 0:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001916 self.task_fail(task, status, fakerootlog=fakerootlog)
Brad Bishop96ff1982019-08-19 13:50:42 -04001917 else:
1918 self.task_complete(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001919 return True
1920
1921 def finish_now(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001922 for mc in self.rq.worker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001923 try:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001924 RunQueue.send_pickled_data(self.rq.worker[mc].process, b"", "finishnow")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001925 self.rq.worker[mc].process.stdin.flush()
1926 except IOError:
1927 # worker must have died?
1928 pass
1929 for mc in self.rq.fakeworker:
1930 try:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001931 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, b"", "finishnow")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001932 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001933 except IOError:
1934 # worker must have died?
1935 pass
1936
Andrew Geissler595f6302022-01-24 19:11:47 +00001937 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001938 self.rq.state = runQueueFailed
1939 return
1940
1941 self.rq.state = runQueueComplete
1942 return
1943
1944 def finish(self):
1945 self.rq.state = runQueueCleanUp
1946
Andrew Geissler5199d832021-09-24 16:47:35 -05001947 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001948 if active > 0:
1949 bb.event.fire(runQueueExitWait(active), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001950 self.rq.read_workers()
1951 return self.rq.active_fds()
1952
Andrew Geissler595f6302022-01-24 19:11:47 +00001953 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001954 self.rq.state = runQueueFailed
1955 return True
1956
1957 self.rq.state = runQueueComplete
1958 return True
1959
Brad Bishop96ff1982019-08-19 13:50:42 -04001960 # Used by setscene only
1961 def check_dependencies(self, task, taskdeps):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001962 if not self.rq.depvalidate:
1963 return False
1964
Brad Bishop08902b02019-08-20 09:16:51 -04001965 # Must not edit parent data
1966 taskdeps = set(taskdeps)
1967
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001968 taskdata = {}
1969 taskdeps.add(task)
1970 for dep in taskdeps:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001971 (mc, fn, taskname, taskfn) = split_tid_mcfn(dep)
1972 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001973 taskdata[dep] = [pn, taskname, fn]
1974 call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001975 locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001976 valid = bb.utils.better_eval(call, locs)
1977 return valid
1978
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001979 def can_start_task(self):
Andrew Geissler5199d832021-09-24 16:47:35 -05001980 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001981 can_start = active < self.number_tasks
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001982 return can_start
1983
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001984 def get_schedulers(self):
1985 schedulers = set(obj for obj in globals().values()
1986 if type(obj) is type and
1987 issubclass(obj, RunQueueScheduler))
1988
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001989 user_schedulers = self.cfgData.getVar("BB_SCHEDULERS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001990 if user_schedulers:
1991 for sched in user_schedulers.split():
1992 if not "." in sched:
1993 bb.note("Ignoring scheduler '%s' from BB_SCHEDULERS: not an import" % sched)
1994 continue
1995
1996 modname, name = sched.rsplit(".", 1)
1997 try:
1998 module = __import__(modname, fromlist=(name,))
1999 except ImportError as exc:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06002000 bb.fatal("Unable to import scheduler '%s' from '%s': %s" % (name, modname, exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002001 else:
2002 schedulers.add(getattr(module, name))
2003 return schedulers
2004
2005 def setbuildable(self, task):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002006 self.runq_buildable.add(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04002007 self.sched.newbuildable(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002008
2009 def task_completeoutright(self, task):
2010 """
2011 Mark a task as completed
2012 Look at the reverse dependencies and mark any task with
2013 completed dependencies as buildable
2014 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002015 self.runq_complete.add(task)
2016 for revdep in self.rqdata.runtaskentries[task].revdeps:
2017 if revdep in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002018 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002019 if revdep in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002020 continue
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002021 alldeps = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002022 for dep in self.rqdata.runtaskentries[revdep].depends:
2023 if dep not in self.runq_complete:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002024 alldeps = False
2025 break
2026 if alldeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002027 self.setbuildable(revdep)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002028 logger.debug("Marking task %s as buildable", revdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002029
Andrew Geissler8f840682023-07-21 09:09:43 -05002030 found = None
2031 for t in sorted(self.sq_deferred.copy()):
Andrew Geissler5199d832021-09-24 16:47:35 -05002032 if self.sq_deferred[t] == task:
Andrew Geissler8f840682023-07-21 09:09:43 -05002033 # Allow the next deferred task to run. Any other deferred tasks should be deferred after that task.
2034 # We shouldn't allow all to run at once as it is prone to races.
2035 if not found:
2036 bb.debug(1, "Deferred task %s now buildable" % t)
2037 del self.sq_deferred[t]
2038 update_scenequeue_data([t], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
2039 found = t
2040 else:
2041 bb.debug(1, "Deferring %s after %s" % (t, found))
2042 self.sq_deferred[t] = found
Andrew Geissler5199d832021-09-24 16:47:35 -05002043
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002044 def task_complete(self, task):
2045 self.stats.taskCompleted()
2046 bb.event.fire(runQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
2047 self.task_completeoutright(task)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002048 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002049
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002050 def task_fail(self, task, exitcode, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002051 """
2052 Called when a task has failed
2053 Updates the state engine with the failure
2054 """
2055 self.stats.taskFailed()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002056 self.failed_tids.append(task)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002057
Andrew Geissler595f6302022-01-24 19:11:47 +00002058 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002059 if fakerootlog and os.path.exists(fakerootlog):
2060 with open(fakerootlog) as fakeroot_log_file:
2061 fakeroot_failed = False
2062 for line in reversed(fakeroot_log_file.readlines()):
2063 for fakeroot_error in ['mismatch', 'error', 'fatal']:
2064 if fakeroot_error in line.lower():
2065 fakeroot_failed = True
2066 if 'doing new pid setup and server start' in line:
2067 break
Andrew Geissler595f6302022-01-24 19:11:47 +00002068 fakeroot_log.append(line)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002069
2070 if not fakeroot_failed:
Andrew Geissler595f6302022-01-24 19:11:47 +00002071 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002072
Andrew Geissler595f6302022-01-24 19:11:47 +00002073 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 -05002074
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002075 if self.rqdata.taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002076 self.rq.state = runQueueCleanUp
2077
2078 def task_skip(self, task, reason):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002079 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002080 self.setbuildable(task)
2081 bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
2082 self.task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002083 self.stats.taskSkipped()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002084 self.stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002085
Brad Bishop08902b02019-08-20 09:16:51 -04002086 def summarise_scenequeue_errors(self):
2087 err = False
2088 if not self.sqdone:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002089 logger.debug('We could skip tasks %s', "\n".join(sorted(self.scenequeue_covered)))
Andrew Geissler5199d832021-09-24 16:47:35 -05002090 completeevent = sceneQueueComplete(self.stats, self.rq)
Brad Bishop08902b02019-08-20 09:16:51 -04002091 bb.event.fire(completeevent, self.cfgData)
2092 if self.sq_deferred:
2093 logger.error("Scenequeue had deferred entries: %s" % pprint.pformat(self.sq_deferred))
2094 err = True
2095 if self.updated_taskhash_queue:
2096 logger.error("Scenequeue had unprocessed changed taskhash entries: %s" % pprint.pformat(self.updated_taskhash_queue))
2097 err = True
2098 if self.holdoff_tasks:
2099 logger.error("Scenequeue had holdoff tasks: %s" % pprint.pformat(self.holdoff_tasks))
2100 err = True
2101
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002102 for tid in self.scenequeue_covered.intersection(self.scenequeue_notcovered):
2103 # No task should end up in both covered and uncovered, that is a bug.
2104 logger.error("Setscene task %s in both covered and notcovered." % tid)
2105
Brad Bishop08902b02019-08-20 09:16:51 -04002106 for tid in self.rqdata.runq_setscene_tids:
2107 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2108 err = True
2109 logger.error("Setscene Task %s was never marked as covered or not covered" % tid)
2110 if tid not in self.sq_buildable:
2111 err = True
2112 logger.error("Setscene Task %s was never marked as buildable" % tid)
2113 if tid not in self.sq_running:
2114 err = True
2115 logger.error("Setscene Task %s was never marked as running" % tid)
2116
2117 for x in self.rqdata.runtaskentries:
2118 if x not in self.tasks_covered and x not in self.tasks_notcovered:
2119 logger.error("Task %s was never moved from the setscene queue" % x)
2120 err = True
2121 if x not in self.tasks_scenequeue_done:
2122 logger.error("Task %s was never processed by the setscene code" % x)
2123 err = True
Andrew Geissler595f6302022-01-24 19:11:47 +00002124 if not self.rqdata.runtaskentries[x].depends and x not in self.runq_buildable:
Brad Bishop08902b02019-08-20 09:16:51 -04002125 logger.error("Task %s was never marked as buildable by the setscene code" % x)
2126 err = True
2127 return err
2128
2129
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002130 def execute(self):
2131 """
Brad Bishop96ff1982019-08-19 13:50:42 -04002132 Run the tasks in a queue prepared by prepare_runqueue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002133 """
2134
2135 self.rq.read_workers()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002136 if self.updated_taskhash_queue or self.pending_migrations:
2137 self.process_possible_migrations()
2138
2139 if not hasattr(self, "sorted_setscene_tids"):
2140 # Don't want to sort this set every execution
2141 self.sorted_setscene_tids = sorted(self.rqdata.runq_setscene_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002142
Brad Bishop96ff1982019-08-19 13:50:42 -04002143 task = None
2144 if not self.sqdone and self.can_start_task():
2145 # Find the next setscene to run
Andrew Geissler82c905d2020-04-13 13:39:40 -05002146 for nexttask in self.sorted_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002147 if nexttask in self.sq_buildable and nexttask not in self.sq_running and self.sqdata.stamps[nexttask] not in self.build_stamps.values():
Patrick Williams169d7bc2024-01-05 11:33:25 -06002148 if nexttask not in self.sqdata.unskippable and self.sqdata.sq_revdeps[nexttask] and \
2149 nexttask not in self.sq_needed_harddeps and \
2150 self.sqdata.sq_revdeps[nexttask].issubset(self.scenequeue_covered) and \
2151 self.check_dependencies(nexttask, self.sqdata.sq_revdeps[nexttask]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002152 if nexttask not in self.rqdata.target_tids:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002153 logger.debug2("Skipping setscene for task %s" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002154 self.sq_task_skip(nexttask)
2155 self.scenequeue_notneeded.add(nexttask)
2156 if nexttask in self.sq_deferred:
2157 del self.sq_deferred[nexttask]
2158 return True
Patrick Williams169d7bc2024-01-05 11:33:25 -06002159 if nexttask in self.sqdata.sq_harddeps_rev and not self.sqdata.sq_harddeps_rev[nexttask].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2160 logger.debug2("Deferring %s due to hard dependencies" % nexttask)
2161 updated = False
2162 for dep in self.sqdata.sq_harddeps_rev[nexttask]:
2163 if dep not in self.sq_needed_harddeps:
2164 logger.debug2("Enabling task %s as it is a hard dependency" % dep)
2165 self.sq_buildable.add(dep)
2166 self.sq_needed_harddeps.add(dep)
2167 updated = True
2168 if updated:
2169 return True
2170 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002171 # If covered tasks are running, need to wait for them to complete
2172 for t in self.sqdata.sq_covered_tasks[nexttask]:
2173 if t in self.runq_running and t not in self.runq_complete:
2174 continue
Brad Bishop96ff1982019-08-19 13:50:42 -04002175 if nexttask in self.sq_deferred:
2176 if self.sq_deferred[nexttask] not in self.runq_complete:
2177 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002178 logger.debug("Task %s no longer deferred" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002179 del self.sq_deferred[nexttask]
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002180 valid = self.rq.validate_hashes(set([nexttask]), self.cooker.data, 0, False, summary=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002181 if not valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002182 logger.debug("%s didn't become valid, skipping setscene" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002183 self.sq_task_failoutright(nexttask)
2184 return True
Brad Bishop96ff1982019-08-19 13:50:42 -04002185 if nexttask in self.sqdata.outrightfail:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002186 logger.debug2('No package found, so skipping setscene task %s', nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002187 self.sq_task_failoutright(nexttask)
2188 return True
2189 if nexttask in self.sqdata.unskippable:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002190 logger.debug2("Setscene task %s is unskippable" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002191 task = nexttask
2192 break
2193 if task is not None:
2194 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
2195 taskname = taskname + "_setscene"
2196 if self.rq.check_stamp_task(task, taskname_from_tid(task), recurse = True, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002197 logger.debug2('Stamp for underlying task %s is current, so skipping setscene variant', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002198 self.sq_task_failoutright(task)
2199 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002200
Brad Bishop96ff1982019-08-19 13:50:42 -04002201 if self.cooker.configuration.force:
2202 if task in self.rqdata.target_tids:
2203 self.sq_task_failoutright(task)
2204 return True
2205
2206 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002207 logger.debug2('Setscene stamp current task %s, so skip it and its dependencies', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002208 self.sq_task_skip(task)
2209 return True
2210
2211 if self.cooker.configuration.skipsetscene:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002212 logger.debug2('No setscene tasks should be executed. Skipping %s', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002213 self.sq_task_failoutright(task)
2214 return True
2215
Andrew Geissler5199d832021-09-24 16:47:35 -05002216 startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
Brad Bishop96ff1982019-08-19 13:50:42 -04002217 bb.event.fire(startevent, self.cfgData)
2218
Brad Bishop96ff1982019-08-19 13:50:42 -04002219 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002220 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002221 runtask = {
2222 'fn' : taskfn,
2223 'task' : task,
2224 'taskname' : taskname,
2225 'taskhash' : self.rqdata.get_task_hash(task),
2226 'unihash' : self.rqdata.get_task_unihash(task),
2227 'quieterrors' : True,
2228 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002229 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002230 'taskdepdata' : self.sq_build_taskdepdata(task),
2231 'dry_run' : False,
2232 'taskdep': taskdep,
2233 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2234 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2235 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2236 }
2237
Brad Bishop96ff1982019-08-19 13:50:42 -04002238 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
2239 if not mc in self.rq.fakeworker:
2240 self.rq.start_fakeworker(self, mc)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002241 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, runtask, "runtask")
Brad Bishop96ff1982019-08-19 13:50:42 -04002242 self.rq.fakeworker[mc].process.stdin.flush()
2243 else:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002244 RunQueue.send_pickled_data(self.rq.worker[mc].process, runtask, "runtask")
Brad Bishop96ff1982019-08-19 13:50:42 -04002245 self.rq.worker[mc].process.stdin.flush()
2246
Andrew Geissler517393d2023-01-13 08:55:19 -06002247 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002248 self.build_stamps2.append(self.build_stamps[task])
2249 self.sq_running.add(task)
2250 self.sq_live.add(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05002251 self.stats.updateActiveSetscene(len(self.sq_live))
Brad Bishop96ff1982019-08-19 13:50:42 -04002252 if self.can_start_task():
2253 return True
2254
Brad Bishopc68388fc2019-08-26 01:33:31 -04002255 self.update_holdofftasks()
2256
Brad Bishop08902b02019-08-20 09:16:51 -04002257 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 -05002258 hashequiv_logger.verbose("Setscene tasks completed")
Brad Bishop96ff1982019-08-19 13:50:42 -04002259
Brad Bishop08902b02019-08-20 09:16:51 -04002260 err = self.summarise_scenequeue_errors()
Brad Bishop96ff1982019-08-19 13:50:42 -04002261 if err:
2262 self.rq.state = runQueueFailed
2263 return True
2264
2265 if self.cooker.configuration.setsceneonly:
2266 self.rq.state = runQueueComplete
2267 return True
2268 self.sqdone = True
2269
2270 if self.stats.total == 0:
2271 # nothing to do
2272 self.rq.state = runQueueComplete
2273 return True
2274
2275 if self.cooker.configuration.setsceneonly:
2276 task = None
2277 else:
2278 task = self.sched.next()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002279 if task is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002280 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002281
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002282 if self.rqdata.setscene_ignore_tasks is not None:
2283 if self.check_setscene_ignore_tasks(task):
2284 self.task_fail(task, "setscene ignore_tasks")
Brad Bishop96ff1982019-08-19 13:50:42 -04002285 return True
2286
2287 if task in self.tasks_covered:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002288 logger.debug2("Setscene covered task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002289 self.task_skip(task, "covered")
2290 return True
2291
2292 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002293 logger.debug2("Stamp current task %s", task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002294
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002295 self.task_skip(task, "existing")
Andrew Geissler82c905d2020-04-13 13:39:40 -05002296 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002297 return True
2298
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002299 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002300 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2301 startevent = runQueueTaskStarted(task, self.stats, self.rq,
2302 noexec=True)
2303 bb.event.fire(startevent, self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002304 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002305 self.stats.taskActive()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002306 if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Andrew Geissler517393d2023-01-13 08:55:19 -06002307 bb.build.make_stamp_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002308 self.task_complete(task)
2309 return True
2310 else:
2311 startevent = runQueueTaskStarted(task, self.stats, self.rq)
2312 bb.event.fire(startevent, self.cfgData)
2313
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002314 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002315 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002316 runtask = {
2317 'fn' : taskfn,
2318 'task' : task,
2319 'taskname' : taskname,
2320 'taskhash' : self.rqdata.get_task_hash(task),
2321 'unihash' : self.rqdata.get_task_unihash(task),
2322 'quieterrors' : False,
2323 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002324 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002325 'taskdepdata' : self.build_taskdepdata(task),
2326 'dry_run' : self.rqdata.setscene_enforce,
2327 'taskdep': taskdep,
2328 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2329 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2330 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2331 }
2332
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002333 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 -05002334 if not mc in self.rq.fakeworker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002335 try:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002336 self.rq.start_fakeworker(self, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002337 except OSError as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002338 logger.critical("Failed to spawn fakeroot worker to run %s: %s" % (task, str(exc)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002339 self.rq.state = runQueueFailed
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002340 self.stats.taskFailed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002341 return True
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002342 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, runtask, "runtask")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002343 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002344 else:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002345 RunQueue.send_pickled_data(self.rq.worker[mc].process, runtask, "runtask")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002346 self.rq.worker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002347
Andrew Geissler517393d2023-01-13 08:55:19 -06002348 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002349 self.build_stamps2.append(self.build_stamps[task])
2350 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002351 self.stats.taskActive()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002352 if self.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002353 return True
2354
Andrew Geissler595f6302022-01-24 19:11:47 +00002355 if self.stats.active > 0 or self.sq_live:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002356 self.rq.read_workers()
2357 return self.rq.active_fds()
2358
Brad Bishop96ff1982019-08-19 13:50:42 -04002359 # No more tasks can be run. If we have deferred setscene tasks we should run them.
2360 if self.sq_deferred:
Patrick Williams92b42cb2022-09-03 06:53:57 -05002361 deferred_tid = list(self.sq_deferred.keys())[0]
2362 blocking_tid = self.sq_deferred.pop(deferred_tid)
Patrick Williams2390b1b2022-11-03 13:47:49 -05002363 logger.warning("Runqueue deadlocked on deferred tasks, forcing task %s blocked by %s" % (deferred_tid, blocking_tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04002364 return True
2365
Andrew Geissler595f6302022-01-24 19:11:47 +00002366 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002367 self.rq.state = runQueueFailed
2368 return True
2369
2370 # Sanity Checks
Brad Bishop08902b02019-08-20 09:16:51 -04002371 err = self.summarise_scenequeue_errors()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002372 for task in self.rqdata.runtaskentries:
2373 if task not in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002374 logger.error("Task %s never buildable!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002375 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002376 elif task not in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002377 logger.error("Task %s never ran!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002378 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002379 elif task not in self.runq_complete:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002380 logger.error("Task %s never completed!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002381 err = True
2382
2383 if err:
2384 self.rq.state = runQueueFailed
2385 else:
2386 self.rq.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002387
2388 return True
2389
Brad Bishopc68388fc2019-08-26 01:33:31 -04002390 def filtermcdeps(self, task, mc, deps):
Andrew Geissler99467da2019-02-25 18:54:23 -06002391 ret = set()
Andrew Geissler99467da2019-02-25 18:54:23 -06002392 for dep in deps:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002393 thismc = mc_from_tid(dep)
2394 if thismc != mc:
Andrew Geissler99467da2019-02-25 18:54:23 -06002395 continue
2396 ret.add(dep)
2397 return ret
2398
Brad Bishopa34c0302019-09-23 22:34:48 -04002399 # We filter out multiconfig dependencies from taskdepdata we pass to the tasks
Andrew Geissler99467da2019-02-25 18:54:23 -06002400 # as most code can't handle them
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002401 def build_taskdepdata(self, task):
2402 taskdepdata = {}
Brad Bishopc68388fc2019-08-26 01:33:31 -04002403 mc = mc_from_tid(task)
Brad Bishop08902b02019-08-20 09:16:51 -04002404 next = self.rqdata.runtaskentries[task].depends.copy()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002405 next.add(task)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002406 next = self.filtermcdeps(task, mc, next)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002407 while next:
2408 additional = []
2409 for revdep in next:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002410 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2411 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2412 deps = self.rqdata.runtaskentries[revdep].depends
2413 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002414 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002415 unihash = self.rqdata.runtaskentries[revdep].unihash
Brad Bishopc68388fc2019-08-26 01:33:31 -04002416 deps = self.filtermcdeps(task, mc, deps)
Patrick Williamsb542dec2023-06-09 01:26:37 -05002417 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2418 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002419 for revdep2 in deps:
2420 if revdep2 not in taskdepdata:
2421 additional.append(revdep2)
2422 next = additional
2423
2424 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2425 return taskdepdata
2426
Brad Bishop08902b02019-08-20 09:16:51 -04002427 def update_holdofftasks(self):
Brad Bishopc68388fc2019-08-26 01:33:31 -04002428
2429 if not self.holdoff_need_update:
2430 return
2431
2432 notcovered = set(self.scenequeue_notcovered)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002433 notcovered |= self.sqdata.cantskip
Brad Bishopc68388fc2019-08-26 01:33:31 -04002434 for tid in self.scenequeue_notcovered:
2435 notcovered |= self.sqdata.sq_covered_tasks[tid]
2436 notcovered |= self.sqdata.unskippable.difference(self.rqdata.runq_setscene_tids)
2437 notcovered.intersection_update(self.tasks_scenequeue_done)
2438
2439 covered = set(self.scenequeue_covered)
2440 for tid in self.scenequeue_covered:
2441 covered |= self.sqdata.sq_covered_tasks[tid]
2442 covered.difference_update(notcovered)
2443 covered.intersection_update(self.tasks_scenequeue_done)
2444
2445 for tid in notcovered | covered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002446 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002447 self.setbuildable(tid)
2448 elif self.rqdata.runtaskentries[tid].depends.issubset(self.runq_complete):
2449 self.setbuildable(tid)
2450
2451 self.tasks_covered = covered
2452 self.tasks_notcovered = notcovered
2453
Brad Bishop08902b02019-08-20 09:16:51 -04002454 self.holdoff_tasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002455
Brad Bishop08902b02019-08-20 09:16:51 -04002456 for tid in self.rqdata.runq_setscene_tids:
2457 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2458 self.holdoff_tasks.add(tid)
2459
2460 for tid in self.holdoff_tasks.copy():
2461 for dep in self.sqdata.sq_covered_tasks[tid]:
2462 if dep not in self.runq_complete:
2463 self.holdoff_tasks.add(dep)
2464
Brad Bishopc68388fc2019-08-26 01:33:31 -04002465 self.holdoff_need_update = False
2466
Brad Bishop08902b02019-08-20 09:16:51 -04002467 def process_possible_migrations(self):
2468
2469 changed = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002470 toprocess = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002471 for tid, unihash in self.updated_taskhash_queue.copy():
2472 if tid in self.runq_running and tid not in self.runq_complete:
2473 continue
2474
2475 self.updated_taskhash_queue.remove((tid, unihash))
2476
2477 if unihash != self.rqdata.runtaskentries[tid].unihash:
Andrew Geisslerc926e172021-05-07 16:11:35 -05002478 # Make sure we rehash any other tasks with the same task hash that we're deferred against.
2479 torehash = [tid]
2480 for deftid in self.sq_deferred:
2481 if self.sq_deferred[deftid] == tid:
2482 torehash.append(deftid)
2483 for hashtid in torehash:
2484 hashequiv_logger.verbose("Task %s unihash changed to %s" % (hashtid, unihash))
2485 self.rqdata.runtaskentries[hashtid].unihash = unihash
2486 bb.parse.siggen.set_unihash(hashtid, unihash)
2487 toprocess.add(hashtid)
Andrew Geissler78b72792022-06-14 06:47:25 -05002488 if torehash:
2489 # Need to save after set_unihash above
2490 bb.parse.siggen.save_unitaskhashes()
Brad Bishop08902b02019-08-20 09:16:51 -04002491
Andrew Geissler82c905d2020-04-13 13:39:40 -05002492 # Work out all tasks which depend upon these
2493 total = set()
2494 next = set()
2495 for p in toprocess:
2496 next |= self.rqdata.runtaskentries[p].revdeps
2497 while next:
2498 current = next.copy()
2499 total = total | next
2500 next = set()
2501 for ntid in current:
2502 next |= self.rqdata.runtaskentries[ntid].revdeps
2503 next.difference_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002504
Andrew Geissler82c905d2020-04-13 13:39:40 -05002505 # Now iterate those tasks in dependency order to regenerate their taskhash/unihash
2506 next = set()
2507 for p in total:
Andrew Geissler595f6302022-01-24 19:11:47 +00002508 if not self.rqdata.runtaskentries[p].depends:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002509 next.add(p)
2510 elif self.rqdata.runtaskentries[p].depends.isdisjoint(total):
2511 next.add(p)
2512
2513 # When an item doesn't have dependencies in total, we can process it. Drop items from total when handled
2514 while next:
2515 current = next.copy()
2516 next = set()
2517 for tid in current:
Andrew Geissler595f6302022-01-24 19:11:47 +00002518 if self.rqdata.runtaskentries[p].depends and not self.rqdata.runtaskentries[tid].depends.isdisjoint(total):
Andrew Geissler82c905d2020-04-13 13:39:40 -05002519 continue
2520 orighash = self.rqdata.runtaskentries[tid].hash
Andrew Geissler517393d2023-01-13 08:55:19 -06002521 newhash = bb.parse.siggen.get_taskhash(tid, self.rqdata.runtaskentries[tid].depends, self.rqdata.dataCaches)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002522 origuni = self.rqdata.runtaskentries[tid].unihash
2523 newuni = bb.parse.siggen.get_unihash(tid)
2524 # FIXME, need to check it can come from sstate at all for determinism?
2525 remapped = False
2526 if newuni == origuni:
2527 # Nothing to do, we match, skip code below
2528 remapped = True
2529 elif tid in self.scenequeue_covered or tid in self.sq_live:
2530 # Already ran this setscene task or it running. Report the new taskhash
2531 bb.parse.siggen.report_unihash_equiv(tid, newhash, origuni, newuni, self.rqdata.dataCaches)
2532 hashequiv_logger.verbose("Already covered setscene for %s so ignoring rehash (remap)" % (tid))
2533 remapped = True
2534
2535 if not remapped:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002536 #logger.debug("Task %s hash changes: %s->%s %s->%s" % (tid, orighash, newhash, origuni, newuni))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002537 self.rqdata.runtaskentries[tid].hash = newhash
2538 self.rqdata.runtaskentries[tid].unihash = newuni
2539 changed.add(tid)
2540
2541 next |= self.rqdata.runtaskentries[tid].revdeps
2542 total.remove(tid)
2543 next.intersection_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002544
2545 if changed:
2546 for mc in self.rq.worker:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002547 RunQueue.send_pickled_data(self.rq.worker[mc].process, bb.parse.siggen.get_taskhashes(), "newtaskhashes")
Brad Bishop08902b02019-08-20 09:16:51 -04002548 for mc in self.rq.fakeworker:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002549 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, bb.parse.siggen.get_taskhashes(), "newtaskhashes")
Brad Bishop08902b02019-08-20 09:16:51 -04002550
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002551 hashequiv_logger.debug(pprint.pformat("Tasks changed:\n%s" % (changed)))
Brad Bishop08902b02019-08-20 09:16:51 -04002552
2553 for tid in changed:
2554 if tid not in self.rqdata.runq_setscene_tids:
2555 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002556 if tid not in self.pending_migrations:
2557 self.pending_migrations.add(tid)
2558
Andrew Geissler82c905d2020-04-13 13:39:40 -05002559 update_tasks = []
Brad Bishop08902b02019-08-20 09:16:51 -04002560 for tid in self.pending_migrations.copy():
Andrew Geissler82c905d2020-04-13 13:39:40 -05002561 if tid in self.runq_running or tid in self.sq_live:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002562 # Too late, task already running, not much we can do now
2563 self.pending_migrations.remove(tid)
2564 continue
2565
Brad Bishop08902b02019-08-20 09:16:51 -04002566 valid = True
2567 # Check no tasks this covers are running
2568 for dep in self.sqdata.sq_covered_tasks[tid]:
2569 if dep in self.runq_running and dep not in self.runq_complete:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002570 hashequiv_logger.debug2("Task %s is running which blocks setscene for %s from running" % (dep, tid))
Brad Bishop08902b02019-08-20 09:16:51 -04002571 valid = False
2572 break
2573 if not valid:
2574 continue
2575
2576 self.pending_migrations.remove(tid)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002577 changed = True
Brad Bishop08902b02019-08-20 09:16:51 -04002578
2579 if tid in self.tasks_scenequeue_done:
2580 self.tasks_scenequeue_done.remove(tid)
2581 for dep in self.sqdata.sq_covered_tasks[tid]:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002582 if dep in self.runq_complete and dep not in self.runq_tasksrun:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002583 bb.error("Task %s marked as completed but now needing to rerun? Halting build." % dep)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002584 self.failed_tids.append(tid)
2585 self.rq.state = runQueueCleanUp
2586 return
2587
Brad Bishop08902b02019-08-20 09:16:51 -04002588 if dep not in self.runq_complete:
2589 if dep in self.tasks_scenequeue_done and dep not in self.sqdata.unskippable:
2590 self.tasks_scenequeue_done.remove(dep)
2591
2592 if tid in self.sq_buildable:
2593 self.sq_buildable.remove(tid)
2594 if tid in self.sq_running:
2595 self.sq_running.remove(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002596 if tid in self.sqdata.outrightfail:
2597 self.sqdata.outrightfail.remove(tid)
2598 if tid in self.scenequeue_notcovered:
2599 self.scenequeue_notcovered.remove(tid)
2600 if tid in self.scenequeue_covered:
2601 self.scenequeue_covered.remove(tid)
2602 if tid in self.scenequeue_notneeded:
2603 self.scenequeue_notneeded.remove(tid)
2604
2605 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2606 self.sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2607
2608 if tid in self.stampcache:
2609 del self.stampcache[tid]
2610
2611 if tid in self.build_stamps:
2612 del self.build_stamps[tid]
2613
2614 update_tasks.append(tid)
2615
2616 update_tasks2 = []
2617 for tid in update_tasks:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002618 harddepfail = False
Patrick Williams169d7bc2024-01-05 11:33:25 -06002619 for t in self.sqdata.sq_harddeps_rev[tid]:
2620 if t in self.scenequeue_notcovered:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002621 harddepfail = True
2622 break
2623 if not harddepfail and self.sqdata.sq_revdeps[tid].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
Brad Bishop08902b02019-08-20 09:16:51 -04002624 if tid not in self.sq_buildable:
2625 self.sq_buildable.add(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +00002626 if not self.sqdata.sq_revdeps[tid]:
Brad Bishop08902b02019-08-20 09:16:51 -04002627 self.sq_buildable.add(tid)
2628
Andrew Geissler517393d2023-01-13 08:55:19 -06002629 update_tasks2.append((tid, harddepfail, tid in self.sqdata.valid))
Brad Bishop08902b02019-08-20 09:16:51 -04002630
Andrew Geissler517393d2023-01-13 08:55:19 -06002631 if update_tasks2:
Brad Bishop08902b02019-08-20 09:16:51 -04002632 self.sqdone = False
Patrick Williams92b42cb2022-09-03 06:53:57 -05002633 for mc in sorted(self.sqdata.multiconfigs):
Andrew Geissler517393d2023-01-13 08:55:19 -06002634 for tid in sorted([t[0] for t in update_tasks2]):
Patrick Williams92b42cb2022-09-03 06:53:57 -05002635 if mc_from_tid(tid) != mc:
2636 continue
2637 h = pending_hash_index(tid, self.rqdata)
2638 if h in self.sqdata.hashes and tid != self.sqdata.hashes[h]:
2639 self.sq_deferred[tid] = self.sqdata.hashes[h]
2640 bb.note("Deferring %s after %s" % (tid, self.sqdata.hashes[h]))
Andrew Geissler517393d2023-01-13 08:55:19 -06002641 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 -05002642
Andrew Geissler517393d2023-01-13 08:55:19 -06002643 for (tid, harddepfail, origvalid) in update_tasks2:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002644 if tid in self.sqdata.valid and not origvalid:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002645 hashequiv_logger.verbose("Setscene task %s became valid" % tid)
2646 if harddepfail:
Andrew Geissler517393d2023-01-13 08:55:19 -06002647 logger.debug2("%s has an unavailable hard dependency so skipping" % (tid))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002648 self.sq_task_failoutright(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002649
2650 if changed:
Andrew Geissler5199d832021-09-24 16:47:35 -05002651 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Patrick Williams169d7bc2024-01-05 11:33:25 -06002652 self.sq_needed_harddeps = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04002653 self.holdoff_need_update = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002654
Brad Bishop96ff1982019-08-19 13:50:42 -04002655 def scenequeue_updatecounters(self, task, fail=False):
Brad Bishop08902b02019-08-20 09:16:51 -04002656
Patrick Williams169d7bc2024-01-05 11:33:25 -06002657 if fail and task in self.sqdata.sq_harddeps:
2658 for dep in sorted(self.sqdata.sq_harddeps[task]):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002659 if dep in self.scenequeue_covered or dep in self.scenequeue_notcovered:
2660 # dependency could be already processed, e.g. noexec setscene task
2661 continue
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002662 noexec, stamppresent = check_setscene_stamps(dep, self.rqdata, self.rq, self.stampcache)
2663 if noexec or stamppresent:
2664 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002665 logger.debug2("%s was unavailable and is a hard dependency of %s so skipping" % (task, dep))
Brad Bishop96ff1982019-08-19 13:50:42 -04002666 self.sq_task_failoutright(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002667 continue
Patrick Williams169d7bc2024-01-05 11:33:25 -06002668 for dep in sorted(self.sqdata.sq_deps[task]):
Brad Bishop08902b02019-08-20 09:16:51 -04002669 if self.sqdata.sq_revdeps[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2670 if dep not in self.sq_buildable:
2671 self.sq_buildable.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002672
Brad Bishop96ff1982019-08-19 13:50:42 -04002673 next = set([task])
2674 while next:
2675 new = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002676 for t in sorted(next):
Brad Bishop96ff1982019-08-19 13:50:42 -04002677 self.tasks_scenequeue_done.add(t)
2678 # Look down the dependency chain for non-setscene things which this task depends on
2679 # and mark as 'done'
2680 for dep in self.rqdata.runtaskentries[t].depends:
2681 if dep in self.rqdata.runq_setscene_tids or dep in self.tasks_scenequeue_done:
2682 continue
2683 if self.rqdata.runtaskentries[dep].revdeps.issubset(self.tasks_scenequeue_done):
2684 new.add(dep)
Brad Bishop96ff1982019-08-19 13:50:42 -04002685 next = new
2686
Andrew Geissler5199d832021-09-24 16:47:35 -05002687 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002688 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002689
2690 def sq_task_completeoutright(self, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002691 """
2692 Mark a task as completed
2693 Look at the reverse dependencies and mark any task with
2694 completed dependencies as buildable
2695 """
2696
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002697 logger.debug('Found task %s which could be accelerated', task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002698 self.scenequeue_covered.add(task)
2699 self.scenequeue_updatecounters(task)
2700
Brad Bishop96ff1982019-08-19 13:50:42 -04002701 def sq_check_taskfail(self, task):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002702 if self.rqdata.setscene_ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002703 realtask = task.split('_setscene')[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002704 (mc, fn, taskname, taskfn) = split_tid_mcfn(realtask)
2705 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002706 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002707 logger.error('Task %s.%s failed' % (pn, taskname + "_setscene"))
2708 self.rq.state = runQueueCleanUp
2709
Brad Bishop96ff1982019-08-19 13:50:42 -04002710 def sq_task_complete(self, task):
Andrew Geissler5199d832021-09-24 16:47:35 -05002711 bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
Brad Bishop96ff1982019-08-19 13:50:42 -04002712 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002713
Brad Bishop96ff1982019-08-19 13:50:42 -04002714 def sq_task_fail(self, task, result):
Andrew Geissler5199d832021-09-24 16:47:35 -05002715 bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002716 self.scenequeue_notcovered.add(task)
2717 self.scenequeue_updatecounters(task, True)
Brad Bishop96ff1982019-08-19 13:50:42 -04002718 self.sq_check_taskfail(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002719
Brad Bishop96ff1982019-08-19 13:50:42 -04002720 def sq_task_failoutright(self, task):
2721 self.sq_running.add(task)
2722 self.sq_buildable.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002723 self.scenequeue_notcovered.add(task)
2724 self.scenequeue_updatecounters(task, True)
2725
Brad Bishop96ff1982019-08-19 13:50:42 -04002726 def sq_task_skip(self, task):
2727 self.sq_running.add(task)
2728 self.sq_buildable.add(task)
2729 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002730
Brad Bishop96ff1982019-08-19 13:50:42 -04002731 def sq_build_taskdepdata(self, task):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002732 def getsetscenedeps(tid):
2733 deps = set()
2734 (mc, fn, taskname, _) = split_tid_mcfn(tid)
2735 realtid = tid + "_setscene"
2736 idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
2737 for (depname, idependtask) in idepends:
2738 if depname not in self.rqdata.taskData[mc].build_targets:
2739 continue
2740
2741 depfn = self.rqdata.taskData[mc].build_targets[depname][0]
2742 if depfn is None:
2743 continue
2744 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2745 deps.add(deptid)
2746 return deps
2747
2748 taskdepdata = {}
2749 next = getsetscenedeps(task)
2750 next.add(task)
2751 while next:
2752 additional = []
2753 for revdep in next:
2754 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2755 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2756 deps = getsetscenedeps(revdep)
2757 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2758 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002759 unihash = self.rqdata.runtaskentries[revdep].unihash
Patrick Williamsb542dec2023-06-09 01:26:37 -05002760 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2761 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002762 for revdep2 in deps:
2763 if revdep2 not in taskdepdata:
2764 additional.append(revdep2)
2765 next = additional
2766
2767 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2768 return taskdepdata
2769
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002770 def check_setscene_ignore_tasks(self, tid):
2771 # Check task that is going to run against the ignore tasks list
Brad Bishop96ff1982019-08-19 13:50:42 -04002772 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2773 # Ignore covered tasks
2774 if tid in self.tasks_covered:
2775 return False
2776 # Ignore stamped tasks
2777 if self.rq.check_stamp_task(tid, taskname, cache=self.stampcache):
2778 return False
2779 # Ignore noexec tasks
2780 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2781 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2782 return False
2783
2784 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002785 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Brad Bishop96ff1982019-08-19 13:50:42 -04002786 if tid in self.rqdata.runq_setscene_tids:
Andrew Geissler595f6302022-01-24 19:11:47 +00002787 msg = ['Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)]
Brad Bishop96ff1982019-08-19 13:50:42 -04002788 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002789 msg = ['Task %s.%s attempted to execute unexpectedly' % (pn, taskname)]
Andrew Geissler82c905d2020-04-13 13:39:40 -05002790 for t in self.scenequeue_notcovered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002791 msg.append("\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash))
2792 msg.append('\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered))
2793 logger.error("".join(msg))
Brad Bishop96ff1982019-08-19 13:50:42 -04002794 return True
2795 return False
2796
2797class SQData(object):
2798 def __init__(self):
2799 # SceneQueue dependencies
2800 self.sq_deps = {}
2801 # SceneQueue reverse dependencies
2802 self.sq_revdeps = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002803 # Injected inter-setscene task dependencies
2804 self.sq_harddeps = {}
Patrick Williams169d7bc2024-01-05 11:33:25 -06002805 self.sq_harddeps_rev = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002806 # Cache of stamp files so duplicates can't run in parallel
2807 self.stamps = {}
2808 # Setscene tasks directly depended upon by the build
2809 self.unskippable = set()
2810 # List of setscene tasks which aren't present
2811 self.outrightfail = set()
2812 # A list of normal tasks a setscene task covers
2813 self.sq_covered_tasks = {}
2814
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002815def build_scenequeue_data(sqdata, rqdata, sqrq):
Brad Bishop96ff1982019-08-19 13:50:42 -04002816
2817 sq_revdeps = {}
2818 sq_revdeps_squash = {}
2819 sq_collated_deps = {}
2820
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002821 # We can't skip specified target tasks which aren't setscene tasks
2822 sqdata.cantskip = set(rqdata.target_tids)
2823 sqdata.cantskip.difference_update(rqdata.runq_setscene_tids)
2824 sqdata.cantskip.intersection_update(rqdata.runtaskentries)
2825
Brad Bishop96ff1982019-08-19 13:50:42 -04002826 # We need to construct a dependency graph for the setscene functions. Intermediate
2827 # dependencies between the setscene tasks only complicate the code. This code
2828 # therefore aims to collapse the huge runqueue dependency tree into a smaller one
2829 # only containing the setscene functions.
2830
2831 rqdata.init_progress_reporter.next_stage()
2832
2833 # First process the chains up to the first setscene task.
2834 endpoints = {}
2835 for tid in rqdata.runtaskentries:
2836 sq_revdeps[tid] = copy.copy(rqdata.runtaskentries[tid].revdeps)
2837 sq_revdeps_squash[tid] = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00002838 if not sq_revdeps[tid] and tid not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002839 #bb.warn("Added endpoint %s" % (tid))
2840 endpoints[tid] = set()
2841
2842 rqdata.init_progress_reporter.next_stage()
2843
2844 # Secondly process the chains between setscene tasks.
2845 for tid in rqdata.runq_setscene_tids:
2846 sq_collated_deps[tid] = set()
2847 #bb.warn("Added endpoint 2 %s" % (tid))
2848 for dep in rqdata.runtaskentries[tid].depends:
2849 if tid in sq_revdeps[dep]:
2850 sq_revdeps[dep].remove(tid)
2851 if dep not in endpoints:
2852 endpoints[dep] = set()
2853 #bb.warn(" Added endpoint 3 %s" % (dep))
2854 endpoints[dep].add(tid)
2855
2856 rqdata.init_progress_reporter.next_stage()
2857
2858 def process_endpoints(endpoints):
2859 newendpoints = {}
2860 for point, task in endpoints.items():
2861 tasks = set()
2862 if task:
2863 tasks |= task
2864 if sq_revdeps_squash[point]:
2865 tasks |= sq_revdeps_squash[point]
2866 if point not in rqdata.runq_setscene_tids:
2867 for t in tasks:
2868 sq_collated_deps[t].add(point)
2869 sq_revdeps_squash[point] = set()
2870 if point in rqdata.runq_setscene_tids:
2871 sq_revdeps_squash[point] = tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002872 continue
2873 for dep in rqdata.runtaskentries[point].depends:
2874 if point in sq_revdeps[dep]:
2875 sq_revdeps[dep].remove(point)
2876 if tasks:
2877 sq_revdeps_squash[dep] |= tasks
Andrew Geissler595f6302022-01-24 19:11:47 +00002878 if not sq_revdeps[dep] and dep not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002879 newendpoints[dep] = task
Andrew Geissler595f6302022-01-24 19:11:47 +00002880 if newendpoints:
Brad Bishop96ff1982019-08-19 13:50:42 -04002881 process_endpoints(newendpoints)
2882
2883 process_endpoints(endpoints)
2884
2885 rqdata.init_progress_reporter.next_stage()
2886
Brad Bishop08902b02019-08-20 09:16:51 -04002887 # Build a list of tasks which are "unskippable"
2888 # These are direct endpoints referenced by the build upto and including setscene tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002889 # Take the build endpoints (no revdeps) and find the sstate tasks they depend upon
2890 new = True
2891 for tid in rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00002892 if not rqdata.runtaskentries[tid].revdeps:
Brad Bishop96ff1982019-08-19 13:50:42 -04002893 sqdata.unskippable.add(tid)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002894 sqdata.unskippable |= sqdata.cantskip
Brad Bishop96ff1982019-08-19 13:50:42 -04002895 while new:
2896 new = False
Brad Bishop08902b02019-08-20 09:16:51 -04002897 orig = sqdata.unskippable.copy()
2898 for tid in sorted(orig, reverse=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04002899 if tid in rqdata.runq_setscene_tids:
2900 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00002901 if not rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04002902 # These are tasks which have no setscene tasks in their chain, need to mark as directly buildable
Brad Bishop96ff1982019-08-19 13:50:42 -04002903 sqrq.setbuildable(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002904 sqdata.unskippable |= rqdata.runtaskentries[tid].depends
Brad Bishop08902b02019-08-20 09:16:51 -04002905 if sqdata.unskippable != orig:
2906 new = True
2907
2908 sqrq.tasks_scenequeue_done |= sqdata.unskippable.difference(rqdata.runq_setscene_tids)
Brad Bishop96ff1982019-08-19 13:50:42 -04002909
2910 rqdata.init_progress_reporter.next_stage(len(rqdata.runtaskentries))
2911
2912 # Sanity check all dependencies could be changed to setscene task references
2913 for taskcounter, tid in enumerate(rqdata.runtaskentries):
2914 if tid in rqdata.runq_setscene_tids:
2915 pass
Andrew Geissler595f6302022-01-24 19:11:47 +00002916 elif sq_revdeps_squash[tid]:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002917 bb.msg.fatal("RunQueue", "Something went badly wrong during scenequeue generation, halting. Please report this problem.")
Brad Bishop96ff1982019-08-19 13:50:42 -04002918 else:
2919 del sq_revdeps_squash[tid]
2920 rqdata.init_progress_reporter.update(taskcounter)
2921
2922 rqdata.init_progress_reporter.next_stage()
2923
2924 # Resolve setscene inter-task dependencies
2925 # e.g. do_sometask_setscene[depends] = "targetname:do_someothertask_setscene"
2926 # Note that anything explicitly depended upon will have its reverse dependencies removed to avoid circular dependencies
2927 for tid in rqdata.runq_setscene_tids:
2928 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2929 realtid = tid + "_setscene"
2930 idepends = rqdata.taskData[mc].taskentries[realtid].idepends
Andrew Geissler517393d2023-01-13 08:55:19 -06002931 sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2932
Patrick Williams169d7bc2024-01-05 11:33:25 -06002933 sqdata.sq_harddeps_rev[tid] = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002934 for (depname, idependtask) in idepends:
2935
2936 if depname not in rqdata.taskData[mc].build_targets:
2937 continue
2938
2939 depfn = rqdata.taskData[mc].build_targets[depname][0]
2940 if depfn is None:
2941 continue
2942 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2943 if deptid not in rqdata.runtaskentries:
2944 bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (realtid, depfn, idependtask))
2945
Patrick Williams169d7bc2024-01-05 11:33:25 -06002946 logger.debug2("Adding hard setscene dependency %s for %s" % (deptid, tid))
2947
Brad Bishop96ff1982019-08-19 13:50:42 -04002948 if not deptid in sqdata.sq_harddeps:
2949 sqdata.sq_harddeps[deptid] = set()
2950 sqdata.sq_harddeps[deptid].add(tid)
Patrick Williams169d7bc2024-01-05 11:33:25 -06002951 sqdata.sq_harddeps_rev[tid].add(deptid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002952
2953 rqdata.init_progress_reporter.next_stage()
2954
Brad Bishop96ff1982019-08-19 13:50:42 -04002955 rqdata.init_progress_reporter.next_stage()
2956
2957 #for tid in sq_revdeps_squash:
2958 # data = ""
2959 # for dep in sq_revdeps_squash[tid]:
2960 # data = data + "\n %s" % dep
2961 # bb.warn("Task %s_setscene: is %s " % (tid, data))
2962
2963 sqdata.sq_revdeps = sq_revdeps_squash
Brad Bishop96ff1982019-08-19 13:50:42 -04002964 sqdata.sq_covered_tasks = sq_collated_deps
2965
2966 # Build reverse version of revdeps to populate deps structure
2967 for tid in sqdata.sq_revdeps:
2968 sqdata.sq_deps[tid] = set()
2969 for tid in sqdata.sq_revdeps:
2970 for dep in sqdata.sq_revdeps[tid]:
2971 sqdata.sq_deps[dep].add(tid)
2972
2973 rqdata.init_progress_reporter.next_stage()
2974
Brad Bishop00e122a2019-10-05 11:10:57 -04002975 sqdata.multiconfigs = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002976 for tid in sqdata.sq_revdeps:
Brad Bishop00e122a2019-10-05 11:10:57 -04002977 sqdata.multiconfigs.add(mc_from_tid(tid))
Andrew Geissler595f6302022-01-24 19:11:47 +00002978 if not sqdata.sq_revdeps[tid]:
Brad Bishop96ff1982019-08-19 13:50:42 -04002979 sqrq.sq_buildable.add(tid)
2980
Patrick Williams169d7bc2024-01-05 11:33:25 -06002981 rqdata.init_progress_reporter.next_stage()
Brad Bishop96ff1982019-08-19 13:50:42 -04002982
Brad Bishop00e122a2019-10-05 11:10:57 -04002983 sqdata.noexec = set()
2984 sqdata.stamppresent = set()
2985 sqdata.valid = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002986
Patrick Williams213cb262021-08-07 19:21:33 -05002987 sqdata.hashes = {}
2988 sqrq.sq_deferred = {}
2989 for mc in sorted(sqdata.multiconfigs):
2990 for tid in sorted(sqdata.sq_revdeps):
2991 if mc_from_tid(tid) != mc:
2992 continue
2993 h = pending_hash_index(tid, rqdata)
2994 if h not in sqdata.hashes:
2995 sqdata.hashes[h] = tid
2996 else:
2997 sqrq.sq_deferred[tid] = sqdata.hashes[h]
Andrew Geissler8f840682023-07-21 09:09:43 -05002998 bb.debug(1, "Deferring %s after %s" % (tid, sqdata.hashes[h]))
Patrick Williams213cb262021-08-07 19:21:33 -05002999
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003000def check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=False):
3001
3002 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
3003
3004 taskdep = rqdata.dataCaches[mc].task_deps[taskfn]
3005
3006 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Andrew Geissler517393d2023-01-13 08:55:19 -06003007 bb.build.make_stamp_mcfn(taskname + "_setscene", taskfn)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003008 return True, False
3009
3010 if rq.check_stamp_task(tid, taskname + "_setscene", cache=stampcache):
3011 logger.debug2('Setscene stamp current for task %s', tid)
3012 return False, True
3013
3014 if rq.check_stamp_task(tid, taskname, recurse = True, cache=stampcache):
3015 logger.debug2('Normal stamp current for task %s', tid)
3016 return False, True
3017
3018 return False, False
3019
Brad Bishop1d80a2e2019-11-15 16:35:03 -05003020def update_scenequeue_data(tids, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True):
Brad Bishop00e122a2019-10-05 11:10:57 -04003021
3022 tocheck = set()
3023
3024 for tid in sorted(tids):
3025 if tid in sqdata.stamppresent:
3026 sqdata.stamppresent.remove(tid)
3027 if tid in sqdata.valid:
3028 sqdata.valid.remove(tid)
Andrew Geisslerc926e172021-05-07 16:11:35 -05003029 if tid in sqdata.outrightfail:
3030 sqdata.outrightfail.remove(tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04003031
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003032 noexec, stamppresent = check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04003033
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003034 if noexec:
Brad Bishop00e122a2019-10-05 11:10:57 -04003035 sqdata.noexec.add(tid)
3036 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003037 logger.debug2("%s is noexec so skipping setscene" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04003038 continue
3039
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003040 if stamppresent:
Brad Bishop00e122a2019-10-05 11:10:57 -04003041 sqdata.stamppresent.add(tid)
3042 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003043 logger.debug2("%s has a valid stamp, skipping" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04003044 continue
3045
3046 tocheck.add(tid)
3047
Brad Bishop1d80a2e2019-11-15 16:35:03 -05003048 sqdata.valid |= rq.validate_hashes(tocheck, cooker.data, len(sqdata.stamppresent), False, summary=summary)
Brad Bishop00e122a2019-10-05 11:10:57 -04003049
Patrick Williams213cb262021-08-07 19:21:33 -05003050 for tid in tids:
3051 if tid in sqdata.stamppresent:
3052 continue
3053 if tid in sqdata.valid:
3054 continue
3055 if tid in sqdata.noexec:
3056 continue
3057 if tid in sqrq.scenequeue_covered:
3058 continue
3059 if tid in sqrq.scenequeue_notcovered:
3060 continue
3061 if tid in sqrq.sq_deferred:
3062 continue
3063 sqdata.outrightfail.add(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003064 logger.debug2("%s already handled (fallthrough), skipping" % (tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04003065
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003066class TaskFailure(Exception):
3067 """
3068 Exception raised when a task in a runqueue fails
3069 """
3070 def __init__(self, x):
3071 self.args = x
3072
3073
3074class runQueueExitWait(bb.event.Event):
3075 """
3076 Event when waiting for task processes to exit
3077 """
3078
3079 def __init__(self, remain):
3080 self.remain = remain
3081 self.message = "Waiting for %s active tasks to finish" % remain
3082 bb.event.Event.__init__(self)
3083
3084class runQueueEvent(bb.event.Event):
3085 """
3086 Base runQueue event class
3087 """
3088 def __init__(self, task, stats, rq):
3089 self.taskid = task
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003090 self.taskstring = task
3091 self.taskname = taskname_from_tid(task)
3092 self.taskfile = fn_from_tid(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003093 self.taskhash = rq.rqdata.get_task_hash(task)
3094 self.stats = stats.copy()
3095 bb.event.Event.__init__(self)
3096
3097class sceneQueueEvent(runQueueEvent):
3098 """
3099 Base sceneQueue event class
3100 """
3101 def __init__(self, task, stats, rq, noexec=False):
3102 runQueueEvent.__init__(self, task, stats, rq)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003103 self.taskstring = task + "_setscene"
3104 self.taskname = taskname_from_tid(task) + "_setscene"
3105 self.taskfile = fn_from_tid(task)
3106 self.taskhash = rq.rqdata.get_task_hash(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003107
3108class runQueueTaskStarted(runQueueEvent):
3109 """
3110 Event notifying a task was started
3111 """
3112 def __init__(self, task, stats, rq, noexec=False):
3113 runQueueEvent.__init__(self, task, stats, rq)
3114 self.noexec = noexec
3115
3116class sceneQueueTaskStarted(sceneQueueEvent):
3117 """
3118 Event notifying a setscene task was started
3119 """
3120 def __init__(self, task, stats, rq, noexec=False):
3121 sceneQueueEvent.__init__(self, task, stats, rq)
3122 self.noexec = noexec
3123
3124class runQueueTaskFailed(runQueueEvent):
3125 """
3126 Event notifying a task failed
3127 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003128 def __init__(self, task, stats, exitcode, rq, fakeroot_log=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003129 runQueueEvent.__init__(self, task, stats, rq)
3130 self.exitcode = exitcode
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003131 self.fakeroot_log = fakeroot_log
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003132
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003133 def __str__(self):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003134 if self.fakeroot_log:
3135 return "Task (%s) failed with exit code '%s' \nPseudo log:\n%s" % (self.taskstring, self.exitcode, self.fakeroot_log)
3136 else:
3137 return "Task (%s) failed with exit code '%s'" % (self.taskstring, self.exitcode)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003138
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003139class sceneQueueTaskFailed(sceneQueueEvent):
3140 """
3141 Event notifying a setscene task failed
3142 """
3143 def __init__(self, task, stats, exitcode, rq):
3144 sceneQueueEvent.__init__(self, task, stats, rq)
3145 self.exitcode = exitcode
3146
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003147 def __str__(self):
3148 return "Setscene task (%s) failed with exit code '%s' - real task will be run instead" % (self.taskstring, self.exitcode)
3149
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003150class sceneQueueComplete(sceneQueueEvent):
3151 """
3152 Event when all the sceneQueue tasks are complete
3153 """
3154 def __init__(self, stats, rq):
3155 self.stats = stats.copy()
3156 bb.event.Event.__init__(self)
3157
3158class runQueueTaskCompleted(runQueueEvent):
3159 """
3160 Event notifying a task completed
3161 """
3162
3163class sceneQueueTaskCompleted(sceneQueueEvent):
3164 """
3165 Event notifying a setscene task completed
3166 """
3167
3168class runQueueTaskSkipped(runQueueEvent):
3169 """
3170 Event notifying a task was skipped
3171 """
3172 def __init__(self, task, stats, rq, reason):
3173 runQueueEvent.__init__(self, task, stats, rq)
3174 self.reason = reason
3175
Brad Bishop08902b02019-08-20 09:16:51 -04003176class taskUniHashUpdate(bb.event.Event):
3177 """
3178 Base runQueue event class
3179 """
3180 def __init__(self, task, unihash):
3181 self.taskid = task
3182 self.unihash = unihash
3183 bb.event.Event.__init__(self)
3184
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003185class runQueuePipe():
3186 """
3187 Abstraction for a pipe between a worker thread and the server
3188 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003189 def __init__(self, pipein, pipeout, d, rq, rqexec, fakerootlogs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003190 self.input = pipein
3191 if pipeout:
3192 pipeout.close()
3193 bb.utils.nonblockingfd(self.input)
Andrew Geissler220dafd2023-10-04 10:18:08 -05003194 self.queue = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003195 self.d = d
3196 self.rq = rq
3197 self.rqexec = rqexec
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003198 self.fakerootlogs = fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003199
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003200 def read(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003201 for workers, name in [(self.rq.worker, "Worker"), (self.rq.fakeworker, "Fakeroot")]:
3202 for worker in workers.values():
3203 worker.process.poll()
3204 if worker.process.returncode is not None and not self.rq.teardown:
3205 bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, worker.process.pid, str(worker.process.returncode)))
3206 self.rq.finish_runqueue(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003207
3208 start = len(self.queue)
3209 try:
Andrew Geissler220dafd2023-10-04 10:18:08 -05003210 self.queue.extend(self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003211 except (OSError, IOError) as e:
3212 if e.errno != errno.EAGAIN:
3213 raise
3214 end = len(self.queue)
3215 found = True
Andrew Geissler595f6302022-01-24 19:11:47 +00003216 while found and self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003217 found = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003218 index = self.queue.find(b"</event>")
3219 while index != -1 and self.queue.startswith(b"<event>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003220 try:
3221 event = pickle.loads(self.queue[7:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003222 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
3223 if isinstance(e, pickle.UnpicklingError) and "truncated" in str(e):
3224 # The pickled data could contain "</event>" so search for the next occurance
3225 # unpickling again, this should be the only way an unpickle error could occur
3226 index = self.queue.find(b"</event>", index + 1)
3227 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003228 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
3229 bb.event.fire_from_worker(event, self.d)
Brad Bishop08902b02019-08-20 09:16:51 -04003230 if isinstance(event, taskUniHashUpdate):
3231 self.rqexec.updated_taskhash_queue.append((event.taskid, event.unihash))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003232 found = True
3233 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003234 index = self.queue.find(b"</event>")
3235 index = self.queue.find(b"</exitcode>")
3236 while index != -1 and self.queue.startswith(b"<exitcode>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003237 try:
3238 task, status = pickle.loads(self.queue[10:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003239 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003240 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003241 (_, _, _, taskfn) = split_tid_mcfn(task)
3242 fakerootlog = None
3243 if self.fakerootlogs and taskfn and taskfn in self.fakerootlogs:
3244 fakerootlog = self.fakerootlogs[taskfn]
3245 self.rqexec.runqueue_process_waitpid(task, status, fakerootlog=fakerootlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003246 found = True
3247 self.queue = self.queue[index+11:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003248 index = self.queue.find(b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003249 return (end > start)
3250
3251 def close(self):
3252 while self.read():
3253 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00003254 if self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003255 print("Warning, worker left partial message: %s" % self.queue)
3256 self.input.close()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003257
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003258def get_setscene_enforce_ignore_tasks(d, targets):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003259 if d.getVar('BB_SETSCENE_ENFORCE') != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003260 return None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003261 ignore_tasks = (d.getVar("BB_SETSCENE_ENFORCE_IGNORE_TASKS") or "").split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003262 outlist = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003263 for item in ignore_tasks[:]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003264 if item.startswith('%:'):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003265 for (mc, target, task, fn) in targets:
3266 outlist.append(target + ':' + item.split(':')[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003267 else:
3268 outlist.append(item)
3269 return outlist
3270
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003271def check_setscene_enforce_ignore_tasks(pn, taskname, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003272 import fnmatch
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003273 if ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003274 item = '%s:%s' % (pn, taskname)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003275 for ignore_tasks in ignore_tasks:
3276 if fnmatch.fnmatch(item, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003277 return True
3278 return False
3279 return True