blob: 6987de3e291bfd7e420a196f1bf4b9720d2fa816 [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 Williams39653562024-03-01 08:54:02 -0600223 elif self.rq.max_loadfactor:
224 limit = False
225 loadfactor = float(os.getloadavg()[0]) / os.cpu_count()
226 # bb.warn("Comparing %s to %s" % (loadfactor, self.rq.max_loadfactor))
227 if loadfactor > self.rq.max_loadfactor:
228 limit = True
229 if hasattr(self, "loadfactor_limit") and limit != self.loadfactor_limit:
230 bb.note("Load average limiting set to %s as load average: %s - using %s/%s bitbake threads" % (limit, loadfactor, len(self.rq.runq_running.difference(self.rq.runq_complete)), self.rq.number_tasks))
231 self.loadfactor_limit = limit
232 return limit
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500233 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500234
235 def next_buildable_task(self):
236 """
237 Return the id of the first task we find that is buildable
238 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500239 # Once tasks are running we don't need to worry about them again
240 self.buildable.difference_update(self.rq.runq_running)
Brad Bishop08902b02019-08-20 09:16:51 -0400241 buildable = set(self.buildable)
Brad Bishop08902b02019-08-20 09:16:51 -0400242 buildable.difference_update(self.rq.holdoff_tasks)
243 buildable.intersection_update(self.rq.tasks_covered | self.rq.tasks_notcovered)
Brad Bishop96ff1982019-08-19 13:50:42 -0400244 if not buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500245 return None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800246
Patrick Williamsdb4c27e2022-08-05 08:10:29 -0500247 # Bitbake requires that at least one task be active. Only check for pressure if
248 # this is the case, otherwise the pressure limitation could result in no tasks
249 # being active and no new tasks started thereby, at times, breaking the scheduler.
250 if self.rq.stats.active and self.exceeds_max_pressure():
251 return None
252
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800253 # Filter out tasks that have a max number of threads that have been exceeded
254 skip_buildable = {}
255 for running in self.rq.runq_running.difference(self.rq.runq_complete):
256 rtaskname = taskname_from_tid(running)
257 if rtaskname not in self.skip_maxthread:
258 self.skip_maxthread[rtaskname] = self.rq.cfgData.getVarFlag(rtaskname, "number_threads")
259 if not self.skip_maxthread[rtaskname]:
260 continue
261 if rtaskname in skip_buildable:
262 skip_buildable[rtaskname] += 1
263 else:
264 skip_buildable[rtaskname] = 1
265
Brad Bishop96ff1982019-08-19 13:50:42 -0400266 if len(buildable) == 1:
Brad Bishop08902b02019-08-20 09:16:51 -0400267 tid = buildable.pop()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800268 taskname = taskname_from_tid(tid)
269 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
270 return None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600271 stamp = self.stamps[tid]
272 if stamp not in self.rq.build_stamps.values():
273 return tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274
275 if not self.rev_prio_map:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600276 self.rev_prio_map = {}
277 for tid in self.rqdata.runtaskentries:
278 self.rev_prio_map[tid] = self.prio_map.index(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500279
280 best = None
281 bestprio = None
Brad Bishop96ff1982019-08-19 13:50:42 -0400282 for tid in buildable:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600283 prio = self.rev_prio_map[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500284 if bestprio is None or bestprio > prio:
Patrick Williams73bd93f2024-02-20 08:07:48 -0600285 taskname = taskname_from_tid(tid)
286 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
287 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600288 stamp = self.stamps[tid]
289 if stamp in self.rq.build_stamps.values():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500290 continue
291 bestprio = prio
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600292 best = tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500293
294 return best
295
296 def next(self):
297 """
298 Return the id of the task we should build next
299 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800300 if self.rq.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500301 return self.next_buildable_task()
302
Brad Bishop316dfdd2018-06-25 12:45:53 -0400303 def newbuildable(self, task):
Brad Bishop08902b02019-08-20 09:16:51 -0400304 self.buildable.add(task)
305
306 def removebuildable(self, task):
307 self.buildable.remove(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500308
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500309 def describe_task(self, taskid):
310 result = 'ID %s' % taskid
311 if self.rev_prio_map:
312 result = result + (' pri %d' % self.rev_prio_map[taskid])
313 return result
314
315 def dump_prio(self, comment):
316 bb.debug(3, '%s (most important first):\n%s' %
317 (comment,
318 '\n'.join(['%d. %s' % (index + 1, self.describe_task(taskid)) for
319 index, taskid in enumerate(self.prio_map)])))
320
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321class RunQueueSchedulerSpeed(RunQueueScheduler):
322 """
323 A scheduler optimised for speed. The priority map is sorted by task weight,
324 heavier weighted tasks (tasks needed by the most other tasks) are run first.
325 """
326 name = "speed"
327
328 def __init__(self, runqueue, rqdata):
329 """
330 The priority map is sorted by task weight.
331 """
332 RunQueueScheduler.__init__(self, runqueue, rqdata)
333
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600334 weights = {}
335 for tid in self.rqdata.runtaskentries:
336 weight = self.rqdata.runtaskentries[tid].weight
337 if not weight in weights:
338 weights[weight] = []
339 weights[weight].append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500340
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600341 self.prio_map = []
342 for weight in sorted(weights):
343 for w in weights[weight]:
344 self.prio_map.append(w)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500345
346 self.prio_map.reverse()
347
348class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed):
349 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500350 A scheduler optimised to complete .bb files as quickly as possible. The
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351 priority map is sorted by task weight, but then reordered so once a given
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500352 .bb file starts to build, it's completed as quickly as possible by
353 running all tasks related to the same .bb file one after the after.
354 This works well where disk space is at a premium and classes like OE's
355 rm_work are in force.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500356 """
357 name = "completion"
358
359 def __init__(self, runqueue, rqdata):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500360 super(RunQueueSchedulerCompletion, self).__init__(runqueue, rqdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500361
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500362 # Extract list of tasks for each recipe, with tasks sorted
363 # ascending from "must run first" (typically do_fetch) to
364 # "runs last" (do_build). The speed scheduler prioritizes
365 # tasks that must run first before the ones that run later;
366 # this is what we depend on here.
367 task_lists = {}
368 for taskid in self.prio_map:
369 fn, taskname = taskid.rsplit(':', 1)
370 task_lists.setdefault(fn, []).append(taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500371
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500372 # Now unify the different task lists. The strategy is that
373 # common tasks get skipped and new ones get inserted after the
374 # preceeding common one(s) as they are found. Because task
375 # lists should differ only by their number of tasks, but not
376 # the ordering of the common tasks, this should result in a
377 # deterministic result that is a superset of the individual
378 # task ordering.
379 all_tasks = []
380 for recipe, new_tasks in task_lists.items():
381 index = 0
382 old_task = all_tasks[index] if index < len(all_tasks) else None
383 for new_task in new_tasks:
384 if old_task == new_task:
385 # Common task, skip it. This is the fast-path which
386 # avoids a full search.
387 index += 1
388 old_task = all_tasks[index] if index < len(all_tasks) else None
389 else:
390 try:
391 index = all_tasks.index(new_task)
392 # Already present, just not at the current
393 # place. We re-synchronized by changing the
394 # index so that it matches again. Now
395 # move on to the next existing task.
396 index += 1
397 old_task = all_tasks[index] if index < len(all_tasks) else None
398 except ValueError:
399 # Not present. Insert before old_task, which
400 # remains the same (but gets shifted back).
401 all_tasks.insert(index, new_task)
402 index += 1
403 bb.debug(3, 'merged task list: %s' % all_tasks)
404
405 # Now reverse the order so that tasks that finish the work on one
406 # recipe are considered more imporant (= come first). The ordering
407 # is now so that do_build is most important.
408 all_tasks.reverse()
409
410 # Group tasks of the same kind before tasks of less important
411 # kinds at the head of the queue (because earlier = lower
412 # priority number = runs earlier), while preserving the
413 # ordering by recipe. If recipe foo is more important than
414 # bar, then the goal is to work on foo's do_populate_sysroot
415 # before bar's do_populate_sysroot and on the more important
416 # tasks of foo before any of the less important tasks in any
417 # other recipe (if those other recipes are more important than
418 # foo).
419 #
420 # All of this only applies when tasks are runable. Explicit
421 # dependencies still override this ordering by priority.
422 #
423 # Here's an example why this priority re-ordering helps with
424 # minimizing disk usage. Consider a recipe foo with a higher
425 # priority than bar where foo DEPENDS on bar. Then the
426 # implicit rule (from base.bbclass) is that foo's do_configure
427 # depends on bar's do_populate_sysroot. This ensures that
428 # bar's do_populate_sysroot gets done first. Normally the
429 # tasks from foo would continue to run once that is done, and
430 # bar only gets completed and cleaned up later. By ordering
431 # bar's task that depend on bar's do_populate_sysroot before foo's
432 # do_configure, that problem gets avoided.
433 task_index = 0
434 self.dump_prio('original priorities')
435 for task in all_tasks:
436 for index in range(task_index, self.numTasks):
437 taskid = self.prio_map[index]
438 taskname = taskid.rsplit(':', 1)[1]
439 if taskname == task:
440 del self.prio_map[index]
441 self.prio_map.insert(task_index, taskid)
442 task_index += 1
443 self.dump_prio('completion priorities')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600445class RunTaskEntry(object):
446 def __init__(self):
447 self.depends = set()
448 self.revdeps = set()
449 self.hash = None
Brad Bishop19323692019-04-05 15:28:33 -0400450 self.unihash = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600451 self.task = None
452 self.weight = 1
453
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500454class RunQueueData:
455 """
456 BitBake Run Queue implementation
457 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600458 def __init__(self, rq, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500459 self.cooker = cooker
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600460 self.dataCaches = dataCaches
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500461 self.taskData = taskData
462 self.targets = targets
463 self.rq = rq
464 self.warn_multi_bb = False
465
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000466 self.multi_provider_allowed = (cfgData.getVar("BB_MULTI_PROVIDER_ALLOWED") or "").split()
467 self.setscene_ignore_tasks = get_setscene_enforce_ignore_tasks(cfgData, targets)
468 self.setscene_ignore_tasks_checked = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500469 self.setscene_enforce = (cfgData.getVar('BB_SETSCENE_ENFORCE') == "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600470 self.init_progress_reporter = bb.progress.DummyMultiStageProcessProgressReporter()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471
472 self.reset()
473
474 def reset(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600475 self.runtaskentries = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500476
477 def runq_depends_names(self, ids):
478 import re
479 ret = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600480 for id in ids:
481 nam = os.path.basename(id)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500482 nam = re.sub("_[^,]*,", ",", nam)
483 ret.extend([nam])
484 return ret
485
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600486 def get_task_hash(self, tid):
487 return self.runtaskentries[tid].hash
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488
Brad Bishop19323692019-04-05 15:28:33 -0400489 def get_task_unihash(self, tid):
490 return self.runtaskentries[tid].unihash
491
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600492 def get_user_idstring(self, tid, task_name_suffix = ""):
493 return tid + task_name_suffix
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500494
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500495 def get_short_user_idstring(self, task, task_name_suffix = ""):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500496 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
497 pn = self.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600498 taskname = taskname_from_tid(task) + task_name_suffix
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500499 return "%s:%s" % (pn, taskname)
500
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500501 def circular_depchains_handler(self, tasks):
502 """
503 Some tasks aren't buildable, likely due to circular dependency issues.
504 Identify the circular dependencies and print them in a user readable format.
505 """
506 from copy import deepcopy
507
508 valid_chains = []
509 explored_deps = {}
510 msgs = []
511
Andrew Geissler99467da2019-02-25 18:54:23 -0600512 class TooManyLoops(Exception):
513 pass
514
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500515 def chain_reorder(chain):
516 """
517 Reorder a dependency chain so the lowest task id is first
518 """
519 lowest = 0
520 new_chain = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600521 for entry in range(len(chain)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500522 if chain[entry] < chain[lowest]:
523 lowest = entry
524 new_chain.extend(chain[lowest:])
525 new_chain.extend(chain[:lowest])
526 return new_chain
527
528 def chain_compare_equal(chain1, chain2):
529 """
530 Compare two dependency chains and see if they're the same
531 """
532 if len(chain1) != len(chain2):
533 return False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600534 for index in range(len(chain1)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500535 if chain1[index] != chain2[index]:
536 return False
537 return True
538
539 def chain_array_contains(chain, chain_array):
540 """
541 Return True if chain_array contains chain
542 """
543 for ch in chain_array:
544 if chain_compare_equal(ch, chain):
545 return True
546 return False
547
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600548 def find_chains(tid, prev_chain):
549 prev_chain.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500550 total_deps = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600551 total_deps.extend(self.runtaskentries[tid].revdeps)
552 for revdep in self.runtaskentries[tid].revdeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500553 if revdep in prev_chain:
554 idx = prev_chain.index(revdep)
555 # To prevent duplicates, reorder the chain to start with the lowest taskid
556 # and search through an array of those we've already printed
557 chain = prev_chain[idx:]
558 new_chain = chain_reorder(chain)
559 if not chain_array_contains(new_chain, valid_chains):
560 valid_chains.append(new_chain)
561 msgs.append("Dependency loop #%d found:\n" % len(valid_chains))
562 for dep in new_chain:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600563 msgs.append(" Task %s (dependent Tasks %s)\n" % (dep, self.runq_depends_names(self.runtaskentries[dep].depends)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500564 msgs.append("\n")
565 if len(valid_chains) > 10:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000566 msgs.append("Halted dependency loops search after 10 matches.\n")
Andrew Geissler99467da2019-02-25 18:54:23 -0600567 raise TooManyLoops
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500568 continue
569 scan = False
570 if revdep not in explored_deps:
571 scan = True
572 elif revdep in explored_deps[revdep]:
573 scan = True
574 else:
575 for dep in prev_chain:
576 if dep in explored_deps[revdep]:
577 scan = True
578 if scan:
579 find_chains(revdep, copy.deepcopy(prev_chain))
580 for dep in explored_deps[revdep]:
581 if dep not in total_deps:
582 total_deps.append(dep)
583
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600584 explored_deps[tid] = total_deps
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500585
Andrew Geissler99467da2019-02-25 18:54:23 -0600586 try:
587 for task in tasks:
588 find_chains(task, [])
589 except TooManyLoops:
590 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500591
592 return msgs
593
594 def calculate_task_weights(self, endpoints):
595 """
596 Calculate a number representing the "weight" of each task. Heavier weighted tasks
597 have more dependencies and hence should be executed sooner for maximum speed.
598
599 This function also sanity checks the task list finding tasks that are not
600 possible to execute due to circular dependencies.
601 """
602
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600603 numTasks = len(self.runtaskentries)
604 weight = {}
605 deps_left = {}
606 task_done = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500607
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600608 for tid in self.runtaskentries:
609 task_done[tid] = False
610 weight[tid] = 1
611 deps_left[tid] = len(self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500612
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600613 for tid in endpoints:
614 weight[tid] = 10
615 task_done[tid] = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616
617 while True:
618 next_points = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600619 for tid in endpoints:
620 for revdep in self.runtaskentries[tid].depends:
621 weight[revdep] = weight[revdep] + weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500622 deps_left[revdep] = deps_left[revdep] - 1
623 if deps_left[revdep] == 0:
624 next_points.append(revdep)
625 task_done[revdep] = True
626 endpoints = next_points
Andrew Geissler595f6302022-01-24 19:11:47 +0000627 if not next_points:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500628 break
629
630 # Circular dependency sanity check
631 problem_tasks = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600632 for tid in self.runtaskentries:
633 if task_done[tid] is False or deps_left[tid] != 0:
634 problem_tasks.append(tid)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600635 logger.debug2("Task %s is not buildable", tid)
636 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 -0600637 self.runtaskentries[tid].weight = weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500638
639 if problem_tasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600640 message = "%s unbuildable tasks were found.\n" % len(problem_tasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500641 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"
642 message = message + "Identifying dependency loops (this may take a short while)...\n"
643 logger.error(message)
644
645 msgs = self.circular_depchains_handler(problem_tasks)
646
647 message = "\n"
648 for msg in msgs:
649 message = message + msg
650 bb.msg.fatal("RunQueue", message)
651
652 return weight
653
654 def prepare(self):
655 """
656 Turn a set of taskData into a RunQueue and compute data needed
657 to optimise the execution order.
658 """
659
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600660 runq_build = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500661 recursivetasks = {}
662 recursiveitasks = {}
663 recursivetasksselfref = set()
664
665 taskData = self.taskData
666
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600667 found = False
668 for mc in self.taskData:
Andrew Geissler595f6302022-01-24 19:11:47 +0000669 if taskData[mc].taskentries:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600670 found = True
671 break
672 if not found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500673 # Nothing to do
674 return 0
675
Andrew Geissler517393d2023-01-13 08:55:19 -0600676 bb.parse.siggen.setup_datacache(self.dataCaches)
677
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600678 self.init_progress_reporter.start()
679 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600680 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500681
682 # Step A - Work out a list of tasks to run
683 #
684 # Taskdata gives us a list of possible providers for every build and run
685 # target ordered by priority. It also gives information on each of those
686 # providers.
687 #
688 # To create the actual list of tasks to execute we fix the list of
689 # providers and then resolve the dependencies into task IDs. This
690 # process is repeated for each type of dependency (tdepends, deptask,
691 # rdeptast, recrdeptask, idepends).
692
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600693 def add_build_dependencies(depids, tasknames, depends, mc):
694 for depname in depids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500695 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600696 if depname not in taskData[mc].build_targets or not taskData[mc].build_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500697 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600698 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500699 if depdata is None:
700 continue
701 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600702 t = depdata + ":" + taskname
703 if t in taskData[mc].taskentries:
704 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500705
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600706 def add_runtime_dependencies(depids, tasknames, depends, mc):
707 for depname in depids:
708 if depname not in taskData[mc].run_targets or not taskData[mc].run_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500709 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600710 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500711 if depdata is None:
712 continue
713 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600714 t = depdata + ":" + taskname
715 if t in taskData[mc].taskentries:
716 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500717
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800718 def add_mc_dependencies(mc, tid):
719 mcdeps = taskData[mc].get_mcdepends()
720 for dep in mcdeps:
721 mcdependency = dep.split(':')
722 pn = mcdependency[3]
723 frommc = mcdependency[1]
724 mcdep = mcdependency[2]
725 deptask = mcdependency[4]
Andrew Geissler517393d2023-01-13 08:55:19 -0600726 if mcdep not in taskData:
727 bb.fatal("Multiconfig '%s' is referenced in multiconfig dependency '%s' but not enabled in BBMULTICONFIG?" % (mcdep, dep))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800728 if mc == frommc:
729 fn = taskData[mcdep].build_targets[pn][0]
730 newdep = '%s:%s' % (fn,deptask)
731 taskData[mc].taskentries[tid].tdepends.append(newdep)
732
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600733 for mc in taskData:
734 for tid in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500735
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600736 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
737 #runtid = build_tid(mc, fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500738
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600739 #logger.debug2("Processing %s,%s:%s", mc, fn, taskname)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600740
741 depends = set()
742 task_deps = self.dataCaches[mc].task_deps[taskfn]
743
744 self.runtaskentries[tid] = RunTaskEntry()
745
746 if fn in taskData[mc].failed_fns:
747 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500748
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800749 # We add multiconfig dependencies before processing internal task deps (tdepends)
750 if 'mcdepends' in task_deps and taskname in task_deps['mcdepends']:
751 add_mc_dependencies(mc, tid)
752
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500753 # Resolve task internal dependencies
754 #
755 # e.g. addtask before X after Y
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600756 for t in taskData[mc].taskentries[tid].tdepends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800757 (depmc, depfn, deptaskname, _) = split_tid_mcfn(t)
758 depends.add(build_tid(depmc, depfn, deptaskname))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759
760 # Resolve 'deptask' dependencies
761 #
762 # e.g. do_sometask[deptask] = "do_someothertask"
763 # (makes sure sometask runs after someothertask of all DEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600764 if 'deptask' in task_deps and taskname in task_deps['deptask']:
765 tasknames = task_deps['deptask'][taskname].split()
766 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500767
768 # Resolve 'rdeptask' dependencies
769 #
770 # e.g. do_sometask[rdeptask] = "do_someothertask"
771 # (makes sure sometask runs after someothertask of all RDEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600772 if 'rdeptask' in task_deps and taskname in task_deps['rdeptask']:
773 tasknames = task_deps['rdeptask'][taskname].split()
774 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775
776 # Resolve inter-task dependencies
777 #
778 # e.g. do_sometask[depends] = "targetname:do_someothertask"
779 # (makes sure sometask runs after targetname's someothertask)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600780 idepends = taskData[mc].taskentries[tid].idepends
781 for (depname, idependtask) in idepends:
782 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 -0500783 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600784 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500785 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600786 t = depdata + ":" + idependtask
787 depends.add(t)
788 if t not in taskData[mc].taskentries:
789 bb.msg.fatal("RunQueue", "Task %s in %s depends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
790 irdepends = taskData[mc].taskentries[tid].irdepends
791 for (depname, idependtask) in irdepends:
792 if depname in taskData[mc].run_targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500793 # Won't be in run_targets if ASSUME_PROVIDED
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500794 if not taskData[mc].run_targets[depname]:
795 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600796 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500797 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 t = depdata + ":" + idependtask
799 depends.add(t)
800 if t not in taskData[mc].taskentries:
801 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 -0500802
803 # Resolve recursive 'recrdeptask' dependencies (Part A)
804 #
805 # e.g. do_sometask[recrdeptask] = "do_someothertask"
806 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
807 # We cover the recursive part of the dependencies below
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600808 if 'recrdeptask' in task_deps and taskname in task_deps['recrdeptask']:
809 tasknames = task_deps['recrdeptask'][taskname].split()
810 recursivetasks[tid] = tasknames
811 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
812 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
813 if taskname in tasknames:
814 recursivetasksselfref.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500815
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600816 if 'recideptask' in task_deps and taskname in task_deps['recideptask']:
817 recursiveitasks[tid] = []
818 for t in task_deps['recideptask'][taskname].split():
819 newdep = build_tid(mc, fn, t)
820 recursiveitasks[tid].append(newdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600822 self.runtaskentries[tid].depends = depends
Brad Bishop316dfdd2018-06-25 12:45:53 -0400823 # Remove all self references
824 self.runtaskentries[tid].depends.discard(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500825
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600826 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500827
Brad Bishop316dfdd2018-06-25 12:45:53 -0400828 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600829 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400830
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500831 # Resolve recursive 'recrdeptask' dependencies (Part B)
832 #
833 # e.g. do_sometask[recrdeptask] = "do_someothertask"
834 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600835 # 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 -0600836
Brad Bishop316dfdd2018-06-25 12:45:53 -0400837 # Generating/interating recursive lists of dependencies is painful and potentially slow
838 # Precompute recursive task dependencies here by:
839 # a) create a temp list of reverse dependencies (revdeps)
840 # b) walk up the ends of the chains (when a given task no longer has dependencies i.e. len(deps) == 0)
841 # c) combine the total list of dependencies in cumulativedeps
842 # d) optimise by pre-truncating 'task' off the items in cumulativedeps (keeps items in sets lower)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500843
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844
Brad Bishop316dfdd2018-06-25 12:45:53 -0400845 revdeps = {}
846 deps = {}
847 cumulativedeps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600848 for tid in self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400849 deps[tid] = set(self.runtaskentries[tid].depends)
850 revdeps[tid] = set()
851 cumulativedeps[tid] = set()
852 # Generate a temp list of reverse dependencies
853 for tid in self.runtaskentries:
854 for dep in self.runtaskentries[tid].depends:
855 revdeps[dep].add(tid)
856 # Find the dependency chain endpoints
857 endpoints = set()
858 for tid in self.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +0000859 if not deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400860 endpoints.add(tid)
861 # Iterate the chains collating dependencies
862 while endpoints:
863 next = set()
864 for tid in endpoints:
865 for dep in revdeps[tid]:
866 cumulativedeps[dep].add(fn_from_tid(tid))
867 cumulativedeps[dep].update(cumulativedeps[tid])
868 if tid in deps[dep]:
869 deps[dep].remove(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +0000870 if not deps[dep]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400871 next.add(dep)
872 endpoints = next
873 #for tid in deps:
Andrew Geissler595f6302022-01-24 19:11:47 +0000874 # if deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400875 # bb.warn("Sanity test failure, dependencies left for %s (%s)" % (tid, deps[tid]))
876
877 # Loop here since recrdeptasks can depend upon other recrdeptasks and we have to
878 # resolve these recursively until we aren't adding any further extra dependencies
879 extradeps = True
880 while extradeps:
881 extradeps = 0
882 for tid in recursivetasks:
883 tasknames = recursivetasks[tid]
884
885 totaldeps = set(self.runtaskentries[tid].depends)
886 if tid in recursiveitasks:
887 totaldeps.update(recursiveitasks[tid])
888 for dep in recursiveitasks[tid]:
889 if dep not in self.runtaskentries:
890 continue
891 totaldeps.update(self.runtaskentries[dep].depends)
892
893 deps = set()
894 for dep in totaldeps:
895 if dep in cumulativedeps:
896 deps.update(cumulativedeps[dep])
897
898 for t in deps:
899 for taskname in tasknames:
900 newtid = t + ":" + taskname
901 if newtid == tid:
902 continue
903 if newtid in self.runtaskentries and newtid not in self.runtaskentries[tid].depends:
904 extradeps += 1
905 self.runtaskentries[tid].depends.add(newtid)
906
907 # Handle recursive tasks which depend upon other recursive tasks
908 deps = set()
909 for dep in self.runtaskentries[tid].depends.intersection(recursivetasks):
910 deps.update(self.runtaskentries[dep].depends.difference(self.runtaskentries[tid].depends))
911 for newtid in deps:
912 for taskname in tasknames:
913 if not newtid.endswith(":" + taskname):
914 continue
915 if newtid in self.runtaskentries:
916 extradeps += 1
917 self.runtaskentries[tid].depends.add(newtid)
918
919 bb.debug(1, "Added %s recursive dependencies in this loop" % extradeps)
920
921 # Remove recrdeptask circular references so that do_a[recrdeptask] = "do_a do_b" can work
922 for tid in recursivetasksselfref:
923 self.runtaskentries[tid].depends.difference_update(recursivetasksselfref)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600924
925 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600926 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600927
928 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500929
930 # Step B - Mark all active tasks
931 #
932 # Start with the tasks we were asked to run and mark all dependencies
933 # as active too. If the task is to be 'forced', clear its stamp. Once
934 # all active tasks are marked, prune the ones we don't need.
935
936 logger.verbose("Marking Active Tasks")
937
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600938 def mark_active(tid, depth):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500939 """
940 Mark an item as active along with its depends
941 (calls itself recursively)
942 """
943
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600944 if tid in runq_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500945 return
946
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600947 runq_build[tid] = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500948
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600949 depends = self.runtaskentries[tid].depends
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500950 for depend in depends:
951 mark_active(depend, depth+1)
952
Brad Bishop79641f22019-09-10 07:20:22 -0400953 def invalidate_task(tid, error_nostamp):
954 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
955 taskdep = self.dataCaches[mc].task_deps[taskfn]
956 if fn + ":" + taskname not in taskData[mc].taskentries:
957 logger.warning("Task %s does not exist, invalidating this task will have no effect" % taskname)
958 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
959 if error_nostamp:
960 bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
961 else:
962 bb.debug(1, "Task %s is marked nostamp, cannot invalidate this task" % taskname)
963 else:
964 logger.verbose("Invalidate task %s, %s", taskname, fn)
Andrew Geissler517393d2023-01-13 08:55:19 -0600965 bb.parse.siggen.invalidate_task(taskname, taskfn)
Brad Bishop79641f22019-09-10 07:20:22 -0400966
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600967 self.target_tids = []
968 for (mc, target, task, fn) in self.targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500969
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600970 if target not in taskData[mc].build_targets or not taskData[mc].build_targets[target]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500971 continue
972
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600973 if target in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500974 continue
975
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500976 parents = False
977 if task.endswith('-'):
978 parents = True
979 task = task[:-1]
980
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600981 if fn in taskData[mc].failed_fns:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500982 continue
983
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600984 # fn already has mc prefix
985 tid = fn + ":" + task
986 self.target_tids.append(tid)
987 if tid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500988 import difflib
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600989 tasks = []
990 for x in taskData[mc].taskentries:
991 if x.startswith(fn + ":"):
992 tasks.append(taskname_from_tid(x))
993 close_matches = difflib.get_close_matches(task, tasks, cutoff=0.7)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500994 if close_matches:
995 extra = ". Close matches:\n %s" % "\n ".join(close_matches)
996 else:
997 extra = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600998 bb.msg.fatal("RunQueue", "Task %s does not exist for target %s (%s)%s" % (task, target, tid, extra))
999
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001000 # For tasks called "XXXX-", ony run their dependencies
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001001 if parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001002 for i in self.runtaskentries[tid].depends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001003 mark_active(i, 1)
1004 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001005 mark_active(tid, 1)
1006
1007 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001008 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001009
1010 # Step C - Prune all inactive tasks
1011 #
1012 # Once all active tasks are marked, prune the ones we don't need.
1013
Brad Bishop316dfdd2018-06-25 12:45:53 -04001014 # Handle --runall
1015 if self.cooker.configuration.runall:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001016 # re-run the mark_active and then drop unused tasks from new list
Brad Bishop316dfdd2018-06-25 12:45:53 -04001017
Patrick Williams705982a2024-01-12 09:51:57 -06001018 runall_tids = set()
1019 added = True
1020 while added:
1021 reduced_tasklist = set(self.runtaskentries.keys())
1022 for tid in list(self.runtaskentries.keys()):
1023 if tid not in runq_build:
1024 reduced_tasklist.remove(tid)
1025 runq_build = {}
1026
1027 orig = runall_tids
Brad Bishop316dfdd2018-06-25 12:45:53 -04001028 runall_tids = set()
Patrick Williams705982a2024-01-12 09:51:57 -06001029 for task in self.cooker.configuration.runall:
1030 if not task.startswith("do_"):
1031 task = "do_{0}".format(task)
1032 for tid in reduced_tasklist:
1033 wanttid = "{0}:{1}".format(fn_from_tid(tid), task)
1034 if wanttid in self.runtaskentries:
1035 runall_tids.add(wanttid)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001036
Patrick Williams705982a2024-01-12 09:51:57 -06001037 for tid in list(runall_tids):
1038 mark_active(tid, 1)
1039 self.target_tids.append(tid)
1040 if self.cooker.configuration.force:
1041 invalidate_task(tid, False)
1042 added = runall_tids - orig
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001043
Andrew Geissler595f6302022-01-24 19:11:47 +00001044 delcount = set()
1045 for tid in list(self.runtaskentries.keys()):
1046 if tid not in runq_build:
1047 delcount.add(tid)
1048 del self.runtaskentries[tid]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001049
Andrew Geissler595f6302022-01-24 19:11:47 +00001050 if self.cooker.configuration.runall:
1051 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001052 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)))
1053
1054 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001055 bb.event.check_for_interrupts(self.cooker.data)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001056
1057 # Handle runonly
1058 if self.cooker.configuration.runonly:
1059 # re-run the mark_active and then drop unused tasks from new list
1060 runq_build = {}
1061
1062 for task in self.cooker.configuration.runonly:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001063 if not task.startswith("do_"):
1064 task = "do_{0}".format(task)
Andrew Geissler595f6302022-01-24 19:11:47 +00001065 runonly_tids = [k for k in self.runtaskentries.keys() if taskname_from_tid(k) == task]
Brad Bishop316dfdd2018-06-25 12:45:53 -04001066
Andrew Geissler595f6302022-01-24 19:11:47 +00001067 for tid in runonly_tids:
1068 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -04001069 if self.cooker.configuration.force:
1070 invalidate_task(tid, False)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001071
1072 for tid in list(self.runtaskentries.keys()):
1073 if tid not in runq_build:
Andrew Geissler595f6302022-01-24 19:11:47 +00001074 delcount.add(tid)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001075 del self.runtaskentries[tid]
1076
Andrew Geissler595f6302022-01-24 19:11:47 +00001077 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001078 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 -05001079
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001080 #
1081 # Step D - Sanity checks and computation
1082 #
1083
1084 # Check to make sure we still have tasks to run
Andrew Geissler595f6302022-01-24 19:11:47 +00001085 if not self.runtaskentries:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001086 if not taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001087 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.")
1088 else:
1089 bb.msg.fatal("RunQueue", "No active tasks and not in --continue mode?! Please report this bug.")
1090
Brad Bishop316dfdd2018-06-25 12:45:53 -04001091 logger.verbose("Pruned %s inactive tasks, %s left", len(delcount), len(self.runtaskentries))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001092
1093 logger.verbose("Assign Weightings")
1094
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001095 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001096 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001097
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001098 # Generate a list of reverse dependencies to ease future calculations
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001099 for tid in self.runtaskentries:
1100 for dep in self.runtaskentries[tid].depends:
1101 self.runtaskentries[dep].revdeps.add(tid)
1102
1103 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001104 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001105
1106 # Identify tasks at the end of dependency chains
1107 # Error on circular dependency loops (length two)
1108 endpoints = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001109 for tid in self.runtaskentries:
1110 revdeps = self.runtaskentries[tid].revdeps
Andrew Geissler595f6302022-01-24 19:11:47 +00001111 if not revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001112 endpoints.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001113 for dep in revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001114 if dep in self.runtaskentries[tid].depends:
1115 bb.msg.fatal("RunQueue", "Task %s has circular dependency on %s" % (tid, dep))
1116
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001117
1118 logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
1119
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001120 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001121 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001122
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001123 # Calculate task weights
1124 # Check of higher length circular dependencies
1125 self.runq_weight = self.calculate_task_weights(endpoints)
1126
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001127 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001128 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001129
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001130 # Sanity Check - Check for multiple tasks building the same provider
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001131 for mc in self.dataCaches:
1132 prov_list = {}
1133 seen_fn = []
1134 for tid in self.runtaskentries:
1135 (tidmc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1136 if taskfn in seen_fn:
1137 continue
1138 if mc != tidmc:
1139 continue
1140 seen_fn.append(taskfn)
1141 for prov in self.dataCaches[mc].fn_provides[taskfn]:
1142 if prov not in prov_list:
1143 prov_list[prov] = [taskfn]
1144 elif taskfn not in prov_list[prov]:
1145 prov_list[prov].append(taskfn)
1146 for prov in prov_list:
1147 if len(prov_list[prov]) < 2:
1148 continue
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001149 if prov in self.multi_provider_allowed:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001150 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001151 seen_pn = []
1152 # If two versions of the same PN are being built its fatal, we don't support it.
1153 for fn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001154 pn = self.dataCaches[mc].pkg_fn[fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001155 if pn not in seen_pn:
1156 seen_pn.append(pn)
1157 else:
1158 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 +00001159 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 -05001160 #
1161 # Construct a list of things which uniquely depend on each provider
1162 # since this may help the user figure out which dependency is triggering this warning
1163 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001164 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 -05001165 deplist = {}
1166 commondeps = None
1167 for provfn in prov_list[prov]:
1168 deps = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001169 for tid in self.runtaskentries:
1170 fn = fn_from_tid(tid)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001171 if fn != provfn:
1172 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001173 for dep in self.runtaskentries[tid].revdeps:
1174 fn = fn_from_tid(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001175 if fn == provfn:
1176 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001177 deps.add(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001178 if not commondeps:
1179 commondeps = set(deps)
1180 else:
1181 commondeps &= deps
1182 deplist[provfn] = deps
1183 for provfn in deplist:
Andrew Geissler595f6302022-01-24 19:11:47 +00001184 msgs.append("\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001185 #
1186 # Construct a list of provides and runtime providers for each recipe
1187 # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC)
1188 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001189 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 -05001190 provide_results = {}
1191 rprovide_results = {}
1192 commonprovs = None
1193 commonrprovs = None
1194 for provfn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001195 provides = set(self.dataCaches[mc].fn_provides[provfn])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001196 rprovides = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001197 for rprovide in self.dataCaches[mc].rproviders:
1198 if provfn in self.dataCaches[mc].rproviders[rprovide]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001199 rprovides.add(rprovide)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001200 for package in self.dataCaches[mc].packages:
1201 if provfn in self.dataCaches[mc].packages[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001202 rprovides.add(package)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001203 for package in self.dataCaches[mc].packages_dynamic:
1204 if provfn in self.dataCaches[mc].packages_dynamic[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001205 rprovides.add(package)
1206 if not commonprovs:
1207 commonprovs = set(provides)
1208 else:
1209 commonprovs &= provides
1210 provide_results[provfn] = provides
1211 if not commonrprovs:
1212 commonrprovs = set(rprovides)
1213 else:
1214 commonrprovs &= rprovides
1215 rprovide_results[provfn] = rprovides
Andrew Geissler595f6302022-01-24 19:11:47 +00001216 #msgs.append("\nCommon provides:\n %s" % ("\n ".join(commonprovs)))
1217 #msgs.append("\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001218 for provfn in prov_list[prov]:
Andrew Geissler595f6302022-01-24 19:11:47 +00001219 msgs.append("\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs)))
1220 msgs.append("\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001221
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001222 if self.warn_multi_bb:
Andrew Geissler595f6302022-01-24 19:11:47 +00001223 logger.verbnote("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001224 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00001225 logger.error("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001226
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001227 self.init_progress_reporter.next_stage()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001228 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001229 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001230
1231 # Iterate over the task list looking for tasks with a 'setscene' function
Andrew Geissler82c905d2020-04-13 13:39:40 -05001232 self.runq_setscene_tids = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001233 if not self.cooker.configuration.nosetscene:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001234 for tid in self.runtaskentries:
1235 (mc, fn, taskname, _) = split_tid_mcfn(tid)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001236 setscenetid = tid + "_setscene"
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001237 if setscenetid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001238 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -05001239 self.runq_setscene_tids.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001240
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001241 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001242 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001243
1244 # Invalidate task if force mode active
1245 if self.cooker.configuration.force:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001246 for tid in self.target_tids:
1247 invalidate_task(tid, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001248
1249 # Invalidate task if invalidate mode active
1250 if self.cooker.configuration.invalidate_stamp:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001251 for tid in self.target_tids:
1252 fn = fn_from_tid(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001253 for st in self.cooker.configuration.invalidate_stamp.split(','):
1254 if not st.startswith("do_"):
1255 st = "do_%s" % st
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001256 invalidate_task(fn + ":" + st, True)
1257
1258 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001259 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001260
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001261 # Create and print to the logs a virtual/xxxx -> PN (fn) table
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001262 for mc in taskData:
1263 virtmap = taskData[mc].get_providermap(prefix="virtual/")
1264 virtpnmap = {}
1265 for v in virtmap:
1266 virtpnmap[v] = self.dataCaches[mc].pkg_fn[virtmap[v]]
1267 bb.debug(2, "%s resolved to: %s (%s)" % (v, virtpnmap[v], virtmap[v]))
1268 if hasattr(bb.parse.siggen, "tasks_resolved"):
1269 bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
1270
1271 self.init_progress_reporter.next_stage()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001272 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001273
Brad Bishop00e122a2019-10-05 11:10:57 -04001274 bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
1275
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001276 # Iterate over the task list and call into the siggen code
1277 dealtwith = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001278 todeal = set(self.runtaskentries)
Andrew Geissler595f6302022-01-24 19:11:47 +00001279 while todeal:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001280 for tid in todeal.copy():
Andrew Geissler595f6302022-01-24 19:11:47 +00001281 if not (self.runtaskentries[tid].depends - dealtwith):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001282 dealtwith.add(tid)
1283 todeal.remove(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001284 self.prepare_task_hash(tid)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001285 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001286
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001287 bb.parse.siggen.writeout_file_checksum_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001288
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001289 #self.dump_data()
1290 return len(self.runtaskentries)
1291
Brad Bishop19323692019-04-05 15:28:33 -04001292 def prepare_task_hash(self, tid):
Andrew Geissler517393d2023-01-13 08:55:19 -06001293 bb.parse.siggen.prep_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
1294 self.runtaskentries[tid].hash = bb.parse.siggen.get_taskhash(tid, self.runtaskentries[tid].depends, self.dataCaches)
Brad Bishop08902b02019-08-20 09:16:51 -04001295 self.runtaskentries[tid].unihash = bb.parse.siggen.get_unihash(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001296
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001297 def dump_data(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001298 """
1299 Dump some debug information on the internal data structures
1300 """
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001301 logger.debug3("run_tasks:")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001302 for tid in self.runtaskentries:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001303 logger.debug3(" %s: %s Deps %s RevDeps %s", tid,
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001304 self.runtaskentries[tid].weight,
1305 self.runtaskentries[tid].depends,
1306 self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001307
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001308class RunQueueWorker():
1309 def __init__(self, process, pipe):
1310 self.process = process
1311 self.pipe = pipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001312
1313class RunQueue:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001314 def __init__(self, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001315
1316 self.cooker = cooker
1317 self.cfgData = cfgData
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001318 self.rqdata = RunQueueData(self, cooker, cfgData, dataCaches, taskData, targets)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001319
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001320 self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION") or None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001321 self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID") or None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001322
1323 self.state = runQueuePrepare
1324
1325 # For disk space monitor
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001326 # Invoked at regular time intervals via the bitbake heartbeat event
1327 # while the build is running. We generate a unique name for the handler
1328 # here, just in case that there ever is more than one RunQueue instance,
Brad Bishop96ff1982019-08-19 13:50:42 -04001329 # start the handler when reaching runQueueSceneInit, and stop it when
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001330 # done with the build.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001331 self.dm = monitordisk.diskMonitor(cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001332 self.dm_event_handler_name = '_bb_diskmonitor_' + str(id(self))
1333 self.dm_event_handler_registered = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334 self.rqexe = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001335 self.worker = {}
1336 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001337
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001338 @staticmethod
1339 def send_pickled_data(worker, data, name):
1340 msg = bytearray()
1341 msg.extend(b"<" + name.encode() + b">")
1342 pickled_data = pickle.dumps(data)
1343 msg.extend(len(pickled_data).to_bytes(4, 'big'))
1344 msg.extend(pickled_data)
1345 msg.extend(b"</" + name.encode() + b">")
1346 worker.stdin.write(msg)
1347
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001348 def _start_worker(self, mc, fakeroot = False, rqexec = None):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001349 logger.debug("Starting bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001350 magic = "decafbad"
1351 if self.cooker.configuration.profile:
1352 magic = "decafbadbad"
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001353 fakerootlogs = None
Andrew Geissler220dafd2023-10-04 10:18:08 -05001354
1355 workerscript = os.path.realpath(os.path.dirname(__file__) + "/../../bin/bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001356 if fakeroot:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001357 magic = magic + "beef"
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001358 mcdata = self.cooker.databuilder.mcdata[mc]
Brad Bishop19323692019-04-05 15:28:33 -04001359 fakerootcmd = shlex.split(mcdata.getVar("FAKEROOTCMD"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001360 fakerootenv = (mcdata.getVar("FAKEROOTBASEENV") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001361 env = os.environ.copy()
1362 for key, value in (var.split('=') for var in fakerootenv):
1363 env[key] = value
Andrew Geissler220dafd2023-10-04 10:18:08 -05001364 worker = subprocess.Popen(fakerootcmd + [sys.executable, workerscript, magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001365 fakerootlogs = self.rqdata.dataCaches[mc].fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001366 else:
Andrew Geissler220dafd2023-10-04 10:18:08 -05001367 worker = subprocess.Popen([sys.executable, workerscript, magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001368 bb.utils.nonblockingfd(worker.stdout)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001369 workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, self, rqexec, fakerootlogs=fakerootlogs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001370
1371 workerdata = {
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001372 "sigdata" : bb.parse.siggen.get_taskdata(),
Andrew Geissler82c905d2020-04-13 13:39:40 -05001373 "logdefaultlevel" : bb.msg.loggerDefaultLogLevel,
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001374 "build_verbose_shell" : self.cooker.configuration.build_verbose_shell,
1375 "build_verbose_stdout" : self.cooker.configuration.build_verbose_stdout,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001376 "logdefaultdomain" : bb.msg.loggerDefaultDomains,
1377 "prhost" : self.cooker.prhost,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001378 "buildname" : self.cfgData.getVar("BUILDNAME"),
1379 "date" : self.cfgData.getVar("DATE"),
1380 "time" : self.cfgData.getVar("TIME"),
Brad Bishopa34c0302019-09-23 22:34:48 -04001381 "hashservaddr" : self.cooker.hashservaddr,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001382 "umask" : self.cfgData.getVar("BB_DEFAULT_UMASK"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001383 }
1384
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001385 RunQueue.send_pickled_data(worker, self.cooker.configuration, "cookerconfig")
1386 RunQueue.send_pickled_data(worker, self.cooker.extraconfigdata, "extraconfigdata")
1387 RunQueue.send_pickled_data(worker, workerdata, "workerdata")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001388 worker.stdin.flush()
1389
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001390 return RunQueueWorker(worker, workerpipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001391
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001392 def _teardown_worker(self, worker):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001393 if not worker:
1394 return
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001395 logger.debug("Teardown for bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001396 try:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001397 RunQueue.send_pickled_data(worker.process, b"", "quit")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001398 worker.process.stdin.flush()
1399 worker.process.stdin.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001400 except IOError:
1401 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001402 while worker.process.returncode is None:
1403 worker.pipe.read()
1404 worker.process.poll()
1405 while worker.pipe.read():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001406 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001407 worker.pipe.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001408
Patrick Williams169d7bc2024-01-05 11:33:25 -06001409 def start_worker(self, rqexec):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001410 if self.worker:
1411 self.teardown_workers()
1412 self.teardown = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001413 for mc in self.rqdata.dataCaches:
Patrick Williams169d7bc2024-01-05 11:33:25 -06001414 self.worker[mc] = self._start_worker(mc, False, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001415
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001416 def start_fakeworker(self, rqexec, mc):
1417 if not mc in self.fakeworker:
1418 self.fakeworker[mc] = self._start_worker(mc, True, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001419
1420 def teardown_workers(self):
1421 self.teardown = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001422 for mc in self.worker:
1423 self._teardown_worker(self.worker[mc])
1424 self.worker = {}
1425 for mc in self.fakeworker:
1426 self._teardown_worker(self.fakeworker[mc])
1427 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001428
1429 def read_workers(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001430 for mc in self.worker:
1431 self.worker[mc].pipe.read()
1432 for mc in self.fakeworker:
1433 self.fakeworker[mc].pipe.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001434
1435 def active_fds(self):
1436 fds = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001437 for mc in self.worker:
1438 fds.append(self.worker[mc].pipe.input)
1439 for mc in self.fakeworker:
1440 fds.append(self.fakeworker[mc].pipe.input)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001441 return fds
1442
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001443 def check_stamp_task(self, tid, taskname = None, recurse = False, cache = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001444 def get_timestamp(f):
1445 try:
1446 if not os.access(f, os.F_OK):
1447 return None
1448 return os.stat(f)[stat.ST_MTIME]
1449 except:
1450 return None
1451
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001452 (mc, fn, tn, taskfn) = split_tid_mcfn(tid)
1453 if taskname is None:
1454 taskname = tn
1455
Andrew Geissler517393d2023-01-13 08:55:19 -06001456 stampfile = bb.parse.siggen.stampfile_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001457
1458 # If the stamp is missing, it's not current
1459 if not os.access(stampfile, os.F_OK):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001460 logger.debug2("Stampfile %s not available", stampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001461 return False
1462 # If it's a 'nostamp' task, it's not current
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001463 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001464 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001465 logger.debug2("%s.%s is nostamp\n", fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001466 return False
1467
Andrew Geissler517393d2023-01-13 08:55:19 -06001468 if taskname.endswith("_setscene"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001469 return True
1470
1471 if cache is None:
1472 cache = {}
1473
1474 iscurrent = True
1475 t1 = get_timestamp(stampfile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001476 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001477 if iscurrent:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001478 (mc2, fn2, taskname2, taskfn2) = split_tid_mcfn(dep)
Andrew Geissler517393d2023-01-13 08:55:19 -06001479 stampfile2 = bb.parse.siggen.stampfile_mcfn(taskname2, taskfn2)
1480 stampfile3 = bb.parse.siggen.stampfile_mcfn(taskname2 + "_setscene", taskfn2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001481 t2 = get_timestamp(stampfile2)
1482 t3 = get_timestamp(stampfile3)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001483 if t3 and not t2:
1484 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001485 if t3 and t3 > t2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001486 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00001487 if fn == fn2:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001488 if not t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001489 logger.debug2('Stampfile %s does not exist', stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001490 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001491 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001492 if t1 < t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001493 logger.debug2('Stampfile %s < %s', stampfile, stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001494 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001495 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001496 if recurse and iscurrent:
1497 if dep in cache:
1498 iscurrent = cache[dep]
1499 if not iscurrent:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001500 logger.debug2('Stampfile for dependency %s:%s invalid (cached)' % (fn2, taskname2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001501 else:
1502 iscurrent = self.check_stamp_task(dep, recurse=True, cache=cache)
1503 cache[dep] = iscurrent
1504 if recurse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001505 cache[tid] = iscurrent
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001506 return iscurrent
1507
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001508 def validate_hashes(self, tocheck, data, currentcount=0, siginfo=False, summary=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04001509 valid = set()
1510 if self.hashvalidate:
Brad Bishop08902b02019-08-20 09:16:51 -04001511 sq_data = {}
1512 sq_data['hash'] = {}
1513 sq_data['hashfn'] = {}
1514 sq_data['unihash'] = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04001515 for tid in tocheck:
1516 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04001517 sq_data['hash'][tid] = self.rqdata.runtaskentries[tid].hash
1518 sq_data['hashfn'][tid] = self.rqdata.dataCaches[mc].hashfn[taskfn]
1519 sq_data['unihash'][tid] = self.rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -04001520
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001521 valid = self.validate_hash(sq_data, data, siginfo, currentcount, summary)
Brad Bishop96ff1982019-08-19 13:50:42 -04001522
1523 return valid
1524
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001525 def validate_hash(self, sq_data, d, siginfo, currentcount, summary):
1526 locs = {"sq_data" : sq_data, "d" : d, "siginfo" : siginfo, "currentcount" : currentcount, "summary" : summary}
Brad Bishop19323692019-04-05 15:28:33 -04001527
Brad Bishop08902b02019-08-20 09:16:51 -04001528 # Metadata has **kwargs so args can be added, sq_data can also gain new fields
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001529 call = self.hashvalidate + "(sq_data, d, siginfo=siginfo, currentcount=currentcount, summary=summary)"
Brad Bishop19323692019-04-05 15:28:33 -04001530
Brad Bishop19323692019-04-05 15:28:33 -04001531 return bb.utils.better_eval(call, locs)
1532
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001533 def _execute_runqueue(self):
1534 """
1535 Run the tasks in a queue prepared by rqdata.prepare()
1536 Upon failure, optionally try to recover the build using any alternate providers
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001537 (if the halt on failure configuration option isn't set)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001538 """
1539
1540 retval = True
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001541 bb.event.check_for_interrupts(self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001542
1543 if self.state is runQueuePrepare:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001544 # NOTE: if you add, remove or significantly refactor the stages of this
1545 # process then you should recalculate the weightings here. This is quite
1546 # easy to do - just change the next line temporarily to pass debug=True as
1547 # the last parameter and you'll get a printout of the weightings as well
1548 # as a map to the lines where next_stage() was called. Of course this isn't
1549 # critical, but it helps to keep the progress reporting accurate.
1550 self.rqdata.init_progress_reporter = bb.progress.MultiStageProcessProgressReporter(self.cooker.data,
1551 "Initialising tasks",
1552 [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 -05001553 if self.rqdata.prepare() == 0:
1554 self.state = runQueueComplete
1555 else:
1556 self.state = runQueueSceneInit
Brad Bishop00e122a2019-10-05 11:10:57 -04001557 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001558
1559 if self.state is runQueueSceneInit:
Brad Bishop96ff1982019-08-19 13:50:42 -04001560 self.rqdata.init_progress_reporter.next_stage()
1561
1562 # we are ready to run, emit dependency info to any UI or class which
1563 # needs it
1564 depgraph = self.cooker.buildDependTree(self, self.rqdata.taskData)
1565 self.rqdata.init_progress_reporter.next_stage()
1566 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
1567
Brad Bishope2d5b612018-11-23 10:55:50 +13001568 if not self.dm_event_handler_registered:
1569 res = bb.event.register(self.dm_event_handler_name,
Andrew Geissler517393d2023-01-13 08:55:19 -06001570 lambda x, y: self.dm.check(self) if self.state in [runQueueRunning, runQueueCleanUp] else False,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001571 ('bb.event.HeartbeatEvent',), data=self.cfgData)
Brad Bishope2d5b612018-11-23 10:55:50 +13001572 self.dm_event_handler_registered = True
1573
Patrick Williams169d7bc2024-01-05 11:33:25 -06001574 self.rqdata.init_progress_reporter.next_stage()
1575 self.rqexe = RunQueueExecute(self)
1576
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001577 dump = self.cooker.configuration.dump_signatures
1578 if dump:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001579 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001580 if 'printdiff' in dump:
1581 invalidtasks = self.print_diffscenetasks()
1582 self.dump_signatures(dump)
1583 if 'printdiff' in dump:
1584 self.write_diffscenetasks(invalidtasks)
1585 self.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001586
Brad Bishop96ff1982019-08-19 13:50:42 -04001587 if self.state is runQueueSceneInit:
Patrick Williams169d7bc2024-01-05 11:33:25 -06001588 self.start_worker(self.rqexe)
1589 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001590
Brad Bishop96ff1982019-08-19 13:50:42 -04001591 # If we don't have any setscene functions, skip execution
Andrew Geissler595f6302022-01-24 19:11:47 +00001592 if not self.rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04001593 logger.info('No setscene tasks')
1594 for tid in self.rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00001595 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04001596 self.rqexe.setbuildable(tid)
1597 self.rqexe.tasks_notcovered.add(tid)
1598 self.rqexe.sqdone = True
1599 logger.info('Executing Tasks')
1600 self.state = runQueueRunning
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001601
1602 if self.state is runQueueRunning:
1603 retval = self.rqexe.execute()
1604
1605 if self.state is runQueueCleanUp:
1606 retval = self.rqexe.finish()
1607
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001608 build_done = self.state is runQueueComplete or self.state is runQueueFailed
1609
1610 if build_done and self.dm_event_handler_registered:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001611 bb.event.remove(self.dm_event_handler_name, None, data=self.cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001612 self.dm_event_handler_registered = False
1613
1614 if build_done and self.rqexe:
Brad Bishop08902b02019-08-20 09:16:51 -04001615 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001616 self.teardown_workers()
Brad Bishop96ff1982019-08-19 13:50:42 -04001617 if self.rqexe:
1618 if self.rqexe.stats.failed:
1619 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)
1620 else:
1621 # Let's avoid the word "failed" if nothing actually did
1622 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 -05001623
1624 if self.state is runQueueFailed:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001625 raise bb.runqueue.TaskFailure(self.rqexe.failed_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001626
1627 if self.state is runQueueComplete:
1628 # All done
1629 return False
1630
1631 # Loop
1632 return retval
1633
1634 def execute_runqueue(self):
1635 # Catch unexpected exceptions and ensure we exit when an error occurs, not loop.
1636 try:
1637 return self._execute_runqueue()
1638 except bb.runqueue.TaskFailure:
1639 raise
1640 except SystemExit:
1641 raise
1642 except bb.BBHandledException:
1643 try:
1644 self.teardown_workers()
1645 except:
1646 pass
1647 self.state = runQueueComplete
1648 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001649 except Exception as err:
1650 logger.exception("An uncaught exception occurred in runqueue")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001651 try:
1652 self.teardown_workers()
1653 except:
1654 pass
1655 self.state = runQueueComplete
1656 raise
1657
1658 def finish_runqueue(self, now = False):
1659 if not self.rqexe:
1660 self.state = runQueueComplete
1661 return
1662
1663 if now:
1664 self.rqexe.finish_now()
1665 else:
1666 self.rqexe.finish()
1667
Andrew Geissler517393d2023-01-13 08:55:19 -06001668 def _rq_dump_sigtid(self, tids):
1669 for tid in tids:
1670 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1671 dataCaches = self.rqdata.dataCaches
1672 bb.parse.siggen.dump_sigtask(taskfn, taskname, dataCaches[mc].stamp[taskfn], True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001673
1674 def dump_signatures(self, options):
Andrew Geissler517393d2023-01-13 08:55:19 -06001675 if bb.cooker.CookerFeatures.RECIPE_SIGGEN_INFO not in self.cooker.featureset:
1676 bb.fatal("The dump signatures functionality needs the RECIPE_SIGGEN_INFO feature enabled")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001677
Andrew Geissler517393d2023-01-13 08:55:19 -06001678 bb.note("Writing task signature files")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001679
1680 max_process = int(self.cfgData.getVar("BB_NUMBER_PARSE_THREADS") or os.cpu_count() or 1)
Andrew Geissler517393d2023-01-13 08:55:19 -06001681 def chunkify(l, n):
1682 return [l[i::n] for i in range(n)]
1683 tids = chunkify(list(self.rqdata.runtaskentries), max_process)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001684 # We cannot use the real multiprocessing.Pool easily due to some local data
1685 # that can't be pickled. This is a cheap multi-process solution.
1686 launched = []
Andrew Geissler517393d2023-01-13 08:55:19 -06001687 while tids:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001688 if len(launched) < max_process:
Andrew Geissler517393d2023-01-13 08:55:19 -06001689 p = Process(target=self._rq_dump_sigtid, args=(tids.pop(), ))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001690 p.start()
1691 launched.append(p)
1692 for q in launched:
1693 # The finished processes are joined when calling is_alive()
1694 if not q.is_alive():
1695 launched.remove(q)
1696 for p in launched:
1697 p.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001698
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001699 bb.parse.siggen.dump_sigs(self.rqdata.dataCaches, options)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001700
1701 return
1702
1703 def print_diffscenetasks(self):
Patrick Williams705982a2024-01-12 09:51:57 -06001704 def get_root_invalid_tasks(task, taskdepends, valid, noexec, visited_invalid):
1705 invalidtasks = []
1706 for t in taskdepends[task].depends:
1707 if t not in valid and t not in visited_invalid:
1708 invalidtasks.extend(get_root_invalid_tasks(t, taskdepends, valid, noexec, visited_invalid))
1709 visited_invalid.add(t)
1710
1711 direct_invalid = [t for t in taskdepends[task].depends if t not in valid]
1712 if not direct_invalid and task not in noexec:
1713 invalidtasks = [task]
1714 return invalidtasks
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001715
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001716 noexec = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001717 tocheck = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001718
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001719 for tid in self.rqdata.runtaskentries:
1720 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1721 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001722
1723 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001724 noexec.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001725 continue
1726
Brad Bishop96ff1982019-08-19 13:50:42 -04001727 tocheck.add(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001728
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001729 valid_new = self.validate_hashes(tocheck, self.cooker.data, 0, True, summary=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001730
1731 # Tasks which are both setscene and noexec never care about dependencies
1732 # We therefore find tasks which are setscene and noexec and mark their
1733 # unique dependencies as valid.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001734 for tid in noexec:
1735 if tid not in self.rqdata.runq_setscene_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001736 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001737 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001738 hasnoexecparents = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001739 for dep2 in self.rqdata.runtaskentries[dep].revdeps:
1740 if dep2 in self.rqdata.runq_setscene_tids and dep2 in noexec:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001741 continue
1742 hasnoexecparents = False
1743 break
1744 if hasnoexecparents:
1745 valid_new.add(dep)
1746
1747 invalidtasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001748
Patrick Williams705982a2024-01-12 09:51:57 -06001749 toptasks = set(["{}:{}".format(t[3], t[2]) for t in self.rqdata.targets])
1750 for tid in toptasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001751 toprocess = set([tid])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001752 while toprocess:
1753 next = set()
Patrick Williams705982a2024-01-12 09:51:57 -06001754 visited_invalid = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001755 for t in toprocess:
Patrick Williams705982a2024-01-12 09:51:57 -06001756 if t not in valid_new and t not in noexec:
1757 invalidtasks.update(get_root_invalid_tasks(t, self.rqdata.runtaskentries, valid_new, noexec, visited_invalid))
1758 continue
1759 if t in self.rqdata.runq_setscene_tids:
1760 for dep in self.rqexe.sqdata.sq_deps[t]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001761 next.add(dep)
Patrick Williams705982a2024-01-12 09:51:57 -06001762 continue
1763
1764 for dep in self.rqdata.runtaskentries[t].depends:
1765 next.add(dep)
1766
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001767 toprocess = next
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001768
1769 tasklist = []
Patrick Williams705982a2024-01-12 09:51:57 -06001770 for tid in invalidtasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001771 tasklist.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001772
1773 if tasklist:
1774 bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
1775
Patrick Williams705982a2024-01-12 09:51:57 -06001776 return invalidtasks
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001777
1778 def write_diffscenetasks(self, invalidtasks):
Patrick Williams169d7bc2024-01-05 11:33:25 -06001779 bb.siggen.check_siggen_version(bb.siggen)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001780
1781 # Define recursion callback
1782 def recursecb(key, hash1, hash2):
1783 hashes = [hash1, hash2]
Patrick Williams169d7bc2024-01-05 11:33:25 -06001784 bb.debug(1, "Recursively looking for recipe {} hashes {}".format(key, hashes))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001785 hashfiles = bb.siggen.find_siginfo(key, None, hashes, self.cfgData)
Patrick Williams169d7bc2024-01-05 11:33:25 -06001786 bb.debug(1, "Found hashfiles:\n{}".format(hashfiles))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001787
1788 recout = []
1789 if len(hashfiles) == 2:
Patrick Williams169d7bc2024-01-05 11:33:25 -06001790 out2 = bb.siggen.compare_sigfiles(hashfiles[hash1]['path'], hashfiles[hash2]['path'], recursecb)
Brad Bishopc342db32019-05-15 21:57:59 -04001791 recout.extend(list(' ' + l for l in out2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001792 else:
1793 recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
1794
1795 return recout
1796
1797
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001798 for tid in invalidtasks:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001799 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1800 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001801 h = self.rqdata.runtaskentries[tid].unihash
Patrick Williams169d7bc2024-01-05 11:33:25 -06001802 bb.debug(1, "Looking for recipe {} task {}".format(pn, taskname))
Patrick Williams03907ee2022-05-01 06:28:52 -05001803 matches = bb.siggen.find_siginfo(pn, taskname, [], self.cooker.databuilder.mcdata[mc])
Patrick Williams169d7bc2024-01-05 11:33:25 -06001804 bb.debug(1, "Found hashfiles:\n{}".format(matches))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001805 match = None
Patrick Williams169d7bc2024-01-05 11:33:25 -06001806 for m in matches.values():
1807 if h in m['path']:
1808 match = m['path']
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001809 if match is None:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001810 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 -06001811 matches = {k : v for k, v in iter(matches.items()) if h not in k}
Patrick Williams169d7bc2024-01-05 11:33:25 -06001812 matches_local = {k : v for k, v in iter(matches.items()) if h not in k and not v['sstate']}
1813 if matches_local:
1814 matches = matches_local
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001815 if matches:
Patrick Williams169d7bc2024-01-05 11:33:25 -06001816 latestmatch = matches[sorted(matches.keys(), key=lambda h: matches[h]['time'])[-1]]['path']
Brad Bishop19323692019-04-05 15:28:33 -04001817 prevh = __find_sha256__.search(latestmatch).group(0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001818 output = bb.siggen.compare_sigfiles(latestmatch, match, recursecb)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001819 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 -05001820
Brad Bishop96ff1982019-08-19 13:50:42 -04001821
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001822class RunQueueExecute:
1823
1824 def __init__(self, rq):
1825 self.rq = rq
1826 self.cooker = rq.cooker
1827 self.cfgData = rq.cfgData
1828 self.rqdata = rq.rqdata
1829
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001830 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
1831 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001832 self.max_cpu_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_CPU")
1833 self.max_io_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_IO")
Patrick Williams92b42cb2022-09-03 06:53:57 -05001834 self.max_memory_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_MEMORY")
Patrick Williams39653562024-03-01 08:54:02 -06001835 self.max_loadfactor = self.cfgData.getVar("BB_LOADFACTOR_MAX")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001836
Brad Bishop96ff1982019-08-19 13:50:42 -04001837 self.sq_buildable = set()
1838 self.sq_running = set()
1839 self.sq_live = set()
1840
Brad Bishop08902b02019-08-20 09:16:51 -04001841 self.updated_taskhash_queue = []
1842 self.pending_migrations = set()
1843
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001844 self.runq_buildable = set()
1845 self.runq_running = set()
1846 self.runq_complete = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001847 self.runq_tasksrun = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001848
1849 self.build_stamps = {}
1850 self.build_stamps2 = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001851 self.failed_tids = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001852 self.sq_deferred = {}
Patrick Williams169d7bc2024-01-05 11:33:25 -06001853 self.sq_needed_harddeps = set()
Patrick Williams73bd93f2024-02-20 08:07:48 -06001854 self.sq_harddep_deferred = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001855
1856 self.stampcache = {}
1857
Brad Bishop08902b02019-08-20 09:16:51 -04001858 self.holdoff_tasks = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04001859 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04001860 self.sqdone = False
1861
Andrew Geissler5199d832021-09-24 16:47:35 -05001862 self.stats = RunQueueStats(len(self.rqdata.runtaskentries), len(self.rqdata.runq_setscene_tids))
Brad Bishop96ff1982019-08-19 13:50:42 -04001863
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001864 if self.number_tasks <= 0:
1865 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks)
1866
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001867 lower_limit = 1.0
1868 upper_limit = 1000000.0
1869 if self.max_cpu_pressure:
1870 self.max_cpu_pressure = float(self.max_cpu_pressure)
1871 if self.max_cpu_pressure < lower_limit:
1872 bb.fatal("Invalid BB_PRESSURE_MAX_CPU %s, minimum value is %s." % (self.max_cpu_pressure, lower_limit))
1873 if self.max_cpu_pressure > upper_limit:
1874 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))
1875
1876 if self.max_io_pressure:
1877 self.max_io_pressure = float(self.max_io_pressure)
1878 if self.max_io_pressure < lower_limit:
1879 bb.fatal("Invalid BB_PRESSURE_MAX_IO %s, minimum value is %s." % (self.max_io_pressure, lower_limit))
1880 if self.max_io_pressure > upper_limit:
1881 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))
1882
Patrick Williams92b42cb2022-09-03 06:53:57 -05001883 if self.max_memory_pressure:
1884 self.max_memory_pressure = float(self.max_memory_pressure)
1885 if self.max_memory_pressure < lower_limit:
1886 bb.fatal("Invalid BB_PRESSURE_MAX_MEMORY %s, minimum value is %s." % (self.max_memory_pressure, lower_limit))
1887 if self.max_memory_pressure > upper_limit:
1888 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))
Patrick Williams39653562024-03-01 08:54:02 -06001889
1890 if self.max_loadfactor:
1891 self.max_loadfactor = float(self.max_loadfactor)
1892 if self.max_loadfactor <= 0:
1893 bb.fatal("Invalid BB_LOADFACTOR_MAX %s, needs to be greater than zero." % (self.max_loadfactor))
Patrick Williams92b42cb2022-09-03 06:53:57 -05001894
Brad Bishop96ff1982019-08-19 13:50:42 -04001895 # List of setscene tasks which we've covered
1896 self.scenequeue_covered = set()
1897 # List of tasks which are covered (including setscene ones)
1898 self.tasks_covered = set()
1899 self.tasks_scenequeue_done = set()
1900 self.scenequeue_notcovered = set()
1901 self.tasks_notcovered = set()
1902 self.scenequeue_notneeded = set()
1903
Brad Bishop96ff1982019-08-19 13:50:42 -04001904 schedulers = self.get_schedulers()
1905 for scheduler in schedulers:
1906 if self.scheduler == scheduler.name:
1907 self.sched = scheduler(self, self.rqdata)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001908 logger.debug("Using runqueue scheduler '%s'", scheduler.name)
Brad Bishop96ff1982019-08-19 13:50:42 -04001909 break
1910 else:
1911 bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
1912 (self.scheduler, ", ".join(obj.name for obj in schedulers)))
1913
Andrew Geissler595f6302022-01-24 19:11:47 +00001914 #if self.rqdata.runq_setscene_tids:
Brad Bishop08902b02019-08-20 09:16:51 -04001915 self.sqdata = SQData()
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001916 build_scenequeue_data(self.sqdata, self.rqdata, self)
1917
1918 update_scenequeue_data(self.sqdata.sq_revdeps, self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=True)
1919
1920 # Compute a list of 'stale' sstate tasks where the current hash does not match the one
1921 # in any stamp files. Pass the list out to metadata as an event.
1922 found = {}
1923 for tid in self.rqdata.runq_setscene_tids:
1924 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1925 stamps = bb.build.find_stale_stamps(taskname, taskfn)
1926 if stamps:
1927 if mc not in found:
1928 found[mc] = {}
1929 found[mc][tid] = stamps
1930 for mc in found:
1931 event = bb.event.StaleSetSceneTasks(found[mc])
1932 bb.event.fire(event, self.cooker.databuilder.mcdata[mc])
Brad Bishop96ff1982019-08-19 13:50:42 -04001933
Patrick Williams73bd93f2024-02-20 08:07:48 -06001934 self.build_taskdepdata_cache()
1935
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001936 def runqueue_process_waitpid(self, task, status, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001937
1938 # self.build_stamps[pid] may not exist when use shared work directory.
1939 if task in self.build_stamps:
1940 self.build_stamps2.remove(self.build_stamps[task])
1941 del self.build_stamps[task]
1942
Brad Bishop96ff1982019-08-19 13:50:42 -04001943 if task in self.sq_live:
1944 if status != 0:
1945 self.sq_task_fail(task, status)
1946 else:
1947 self.sq_task_complete(task)
1948 self.sq_live.remove(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05001949 self.stats.updateActiveSetscene(len(self.sq_live))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001950 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04001951 if status != 0:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001952 self.task_fail(task, status, fakerootlog=fakerootlog)
Brad Bishop96ff1982019-08-19 13:50:42 -04001953 else:
1954 self.task_complete(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001955 return True
1956
1957 def finish_now(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001958 for mc in self.rq.worker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001959 try:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001960 RunQueue.send_pickled_data(self.rq.worker[mc].process, b"", "finishnow")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001961 self.rq.worker[mc].process.stdin.flush()
1962 except IOError:
1963 # worker must have died?
1964 pass
1965 for mc in self.rq.fakeworker:
1966 try:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001967 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, b"", "finishnow")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001968 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001969 except IOError:
1970 # worker must have died?
1971 pass
1972
Andrew Geissler595f6302022-01-24 19:11:47 +00001973 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001974 self.rq.state = runQueueFailed
1975 return
1976
1977 self.rq.state = runQueueComplete
1978 return
1979
1980 def finish(self):
1981 self.rq.state = runQueueCleanUp
1982
Andrew Geissler5199d832021-09-24 16:47:35 -05001983 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001984 if active > 0:
1985 bb.event.fire(runQueueExitWait(active), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001986 self.rq.read_workers()
1987 return self.rq.active_fds()
1988
Andrew Geissler595f6302022-01-24 19:11:47 +00001989 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001990 self.rq.state = runQueueFailed
1991 return True
1992
1993 self.rq.state = runQueueComplete
1994 return True
1995
Brad Bishop96ff1982019-08-19 13:50:42 -04001996 # Used by setscene only
1997 def check_dependencies(self, task, taskdeps):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001998 if not self.rq.depvalidate:
1999 return False
2000
Brad Bishop08902b02019-08-20 09:16:51 -04002001 # Must not edit parent data
2002 taskdeps = set(taskdeps)
2003
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002004 taskdata = {}
2005 taskdeps.add(task)
2006 for dep in taskdeps:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002007 (mc, fn, taskname, taskfn) = split_tid_mcfn(dep)
2008 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002009 taskdata[dep] = [pn, taskname, fn]
2010 call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002011 locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002012 valid = bb.utils.better_eval(call, locs)
2013 return valid
2014
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002015 def can_start_task(self):
Andrew Geissler5199d832021-09-24 16:47:35 -05002016 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04002017 can_start = active < self.number_tasks
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002018 return can_start
2019
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002020 def get_schedulers(self):
2021 schedulers = set(obj for obj in globals().values()
2022 if type(obj) is type and
2023 issubclass(obj, RunQueueScheduler))
2024
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002025 user_schedulers = self.cfgData.getVar("BB_SCHEDULERS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002026 if user_schedulers:
2027 for sched in user_schedulers.split():
2028 if not "." in sched:
2029 bb.note("Ignoring scheduler '%s' from BB_SCHEDULERS: not an import" % sched)
2030 continue
2031
2032 modname, name = sched.rsplit(".", 1)
2033 try:
2034 module = __import__(modname, fromlist=(name,))
2035 except ImportError as exc:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06002036 bb.fatal("Unable to import scheduler '%s' from '%s': %s" % (name, modname, exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002037 else:
2038 schedulers.add(getattr(module, name))
2039 return schedulers
2040
2041 def setbuildable(self, task):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002042 self.runq_buildable.add(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04002043 self.sched.newbuildable(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002044
2045 def task_completeoutright(self, task):
2046 """
2047 Mark a task as completed
2048 Look at the reverse dependencies and mark any task with
2049 completed dependencies as buildable
2050 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002051 self.runq_complete.add(task)
2052 for revdep in self.rqdata.runtaskentries[task].revdeps:
2053 if revdep in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002054 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002055 if revdep in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002056 continue
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002057 alldeps = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002058 for dep in self.rqdata.runtaskentries[revdep].depends:
2059 if dep not in self.runq_complete:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002060 alldeps = False
2061 break
2062 if alldeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002063 self.setbuildable(revdep)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002064 logger.debug("Marking task %s as buildable", revdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002065
Andrew Geissler8f840682023-07-21 09:09:43 -05002066 found = None
2067 for t in sorted(self.sq_deferred.copy()):
Andrew Geissler5199d832021-09-24 16:47:35 -05002068 if self.sq_deferred[t] == task:
Andrew Geissler8f840682023-07-21 09:09:43 -05002069 # Allow the next deferred task to run. Any other deferred tasks should be deferred after that task.
2070 # We shouldn't allow all to run at once as it is prone to races.
2071 if not found:
2072 bb.debug(1, "Deferred task %s now buildable" % t)
2073 del self.sq_deferred[t]
2074 update_scenequeue_data([t], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
2075 found = t
2076 else:
2077 bb.debug(1, "Deferring %s after %s" % (t, found))
2078 self.sq_deferred[t] = found
Andrew Geissler5199d832021-09-24 16:47:35 -05002079
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002080 def task_complete(self, task):
2081 self.stats.taskCompleted()
2082 bb.event.fire(runQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
2083 self.task_completeoutright(task)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002084 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002085
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002086 def task_fail(self, task, exitcode, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002087 """
2088 Called when a task has failed
2089 Updates the state engine with the failure
2090 """
2091 self.stats.taskFailed()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002092 self.failed_tids.append(task)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002093
Andrew Geissler595f6302022-01-24 19:11:47 +00002094 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002095 if fakerootlog and os.path.exists(fakerootlog):
2096 with open(fakerootlog) as fakeroot_log_file:
2097 fakeroot_failed = False
2098 for line in reversed(fakeroot_log_file.readlines()):
2099 for fakeroot_error in ['mismatch', 'error', 'fatal']:
2100 if fakeroot_error in line.lower():
2101 fakeroot_failed = True
2102 if 'doing new pid setup and server start' in line:
2103 break
Andrew Geissler595f6302022-01-24 19:11:47 +00002104 fakeroot_log.append(line)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002105
2106 if not fakeroot_failed:
Andrew Geissler595f6302022-01-24 19:11:47 +00002107 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002108
Andrew Geissler595f6302022-01-24 19:11:47 +00002109 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 -05002110
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002111 if self.rqdata.taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002112 self.rq.state = runQueueCleanUp
2113
2114 def task_skip(self, task, reason):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002115 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002116 self.setbuildable(task)
2117 bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
2118 self.task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002119 self.stats.taskSkipped()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002120 self.stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002121
Brad Bishop08902b02019-08-20 09:16:51 -04002122 def summarise_scenequeue_errors(self):
2123 err = False
2124 if not self.sqdone:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002125 logger.debug('We could skip tasks %s', "\n".join(sorted(self.scenequeue_covered)))
Andrew Geissler5199d832021-09-24 16:47:35 -05002126 completeevent = sceneQueueComplete(self.stats, self.rq)
Brad Bishop08902b02019-08-20 09:16:51 -04002127 bb.event.fire(completeevent, self.cfgData)
2128 if self.sq_deferred:
2129 logger.error("Scenequeue had deferred entries: %s" % pprint.pformat(self.sq_deferred))
2130 err = True
2131 if self.updated_taskhash_queue:
2132 logger.error("Scenequeue had unprocessed changed taskhash entries: %s" % pprint.pformat(self.updated_taskhash_queue))
2133 err = True
2134 if self.holdoff_tasks:
2135 logger.error("Scenequeue had holdoff tasks: %s" % pprint.pformat(self.holdoff_tasks))
2136 err = True
2137
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002138 for tid in self.scenequeue_covered.intersection(self.scenequeue_notcovered):
2139 # No task should end up in both covered and uncovered, that is a bug.
2140 logger.error("Setscene task %s in both covered and notcovered." % tid)
2141
Brad Bishop08902b02019-08-20 09:16:51 -04002142 for tid in self.rqdata.runq_setscene_tids:
2143 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2144 err = True
2145 logger.error("Setscene Task %s was never marked as covered or not covered" % tid)
2146 if tid not in self.sq_buildable:
2147 err = True
2148 logger.error("Setscene Task %s was never marked as buildable" % tid)
2149 if tid not in self.sq_running:
2150 err = True
2151 logger.error("Setscene Task %s was never marked as running" % tid)
2152
2153 for x in self.rqdata.runtaskentries:
2154 if x not in self.tasks_covered and x not in self.tasks_notcovered:
2155 logger.error("Task %s was never moved from the setscene queue" % x)
2156 err = True
2157 if x not in self.tasks_scenequeue_done:
2158 logger.error("Task %s was never processed by the setscene code" % x)
2159 err = True
Andrew Geissler595f6302022-01-24 19:11:47 +00002160 if not self.rqdata.runtaskentries[x].depends and x not in self.runq_buildable:
Brad Bishop08902b02019-08-20 09:16:51 -04002161 logger.error("Task %s was never marked as buildable by the setscene code" % x)
2162 err = True
2163 return err
2164
2165
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002166 def execute(self):
2167 """
Brad Bishop96ff1982019-08-19 13:50:42 -04002168 Run the tasks in a queue prepared by prepare_runqueue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002169 """
2170
2171 self.rq.read_workers()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002172 if self.updated_taskhash_queue or self.pending_migrations:
2173 self.process_possible_migrations()
2174
2175 if not hasattr(self, "sorted_setscene_tids"):
2176 # Don't want to sort this set every execution
2177 self.sorted_setscene_tids = sorted(self.rqdata.runq_setscene_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002178
Brad Bishop96ff1982019-08-19 13:50:42 -04002179 task = None
2180 if not self.sqdone and self.can_start_task():
2181 # Find the next setscene to run
Andrew Geissler82c905d2020-04-13 13:39:40 -05002182 for nexttask in self.sorted_setscene_tids:
Patrick Williams73bd93f2024-02-20 08:07:48 -06002183 if nexttask in self.sq_buildable and nexttask not in self.sq_running and self.sqdata.stamps[nexttask] not in self.build_stamps.values() and nexttask not in self.sq_harddep_deferred:
Patrick Williams169d7bc2024-01-05 11:33:25 -06002184 if nexttask not in self.sqdata.unskippable and self.sqdata.sq_revdeps[nexttask] and \
2185 nexttask not in self.sq_needed_harddeps and \
2186 self.sqdata.sq_revdeps[nexttask].issubset(self.scenequeue_covered) and \
2187 self.check_dependencies(nexttask, self.sqdata.sq_revdeps[nexttask]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002188 if nexttask not in self.rqdata.target_tids:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002189 logger.debug2("Skipping setscene for task %s" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002190 self.sq_task_skip(nexttask)
2191 self.scenequeue_notneeded.add(nexttask)
2192 if nexttask in self.sq_deferred:
2193 del self.sq_deferred[nexttask]
2194 return True
Patrick Williams169d7bc2024-01-05 11:33:25 -06002195 if nexttask in self.sqdata.sq_harddeps_rev and not self.sqdata.sq_harddeps_rev[nexttask].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2196 logger.debug2("Deferring %s due to hard dependencies" % nexttask)
2197 updated = False
2198 for dep in self.sqdata.sq_harddeps_rev[nexttask]:
2199 if dep not in self.sq_needed_harddeps:
2200 logger.debug2("Enabling task %s as it is a hard dependency" % dep)
2201 self.sq_buildable.add(dep)
2202 self.sq_needed_harddeps.add(dep)
2203 updated = True
Patrick Williams73bd93f2024-02-20 08:07:48 -06002204 self.sq_harddep_deferred.add(nexttask)
Patrick Williams169d7bc2024-01-05 11:33:25 -06002205 if updated:
2206 return True
2207 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002208 # If covered tasks are running, need to wait for them to complete
2209 for t in self.sqdata.sq_covered_tasks[nexttask]:
2210 if t in self.runq_running and t not in self.runq_complete:
2211 continue
Brad Bishop96ff1982019-08-19 13:50:42 -04002212 if nexttask in self.sq_deferred:
2213 if self.sq_deferred[nexttask] not in self.runq_complete:
2214 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002215 logger.debug("Task %s no longer deferred" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002216 del self.sq_deferred[nexttask]
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002217 valid = self.rq.validate_hashes(set([nexttask]), self.cooker.data, 0, False, summary=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002218 if not valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002219 logger.debug("%s didn't become valid, skipping setscene" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002220 self.sq_task_failoutright(nexttask)
2221 return True
Brad Bishop96ff1982019-08-19 13:50:42 -04002222 if nexttask in self.sqdata.outrightfail:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002223 logger.debug2('No package found, so skipping setscene task %s', nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002224 self.sq_task_failoutright(nexttask)
2225 return True
2226 if nexttask in self.sqdata.unskippable:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002227 logger.debug2("Setscene task %s is unskippable" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002228 task = nexttask
2229 break
2230 if task is not None:
2231 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
2232 taskname = taskname + "_setscene"
2233 if self.rq.check_stamp_task(task, taskname_from_tid(task), recurse = True, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002234 logger.debug2('Stamp for underlying task %s is current, so skipping setscene variant', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002235 self.sq_task_failoutright(task)
2236 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002237
Brad Bishop96ff1982019-08-19 13:50:42 -04002238 if self.cooker.configuration.force:
2239 if task in self.rqdata.target_tids:
2240 self.sq_task_failoutright(task)
2241 return True
2242
2243 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002244 logger.debug2('Setscene stamp current task %s, so skip it and its dependencies', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002245 self.sq_task_skip(task)
2246 return True
2247
2248 if self.cooker.configuration.skipsetscene:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002249 logger.debug2('No setscene tasks should be executed. Skipping %s', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002250 self.sq_task_failoutright(task)
2251 return True
2252
Andrew Geissler5199d832021-09-24 16:47:35 -05002253 startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
Brad Bishop96ff1982019-08-19 13:50:42 -04002254 bb.event.fire(startevent, self.cfgData)
2255
Brad Bishop96ff1982019-08-19 13:50:42 -04002256 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002257 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002258 runtask = {
2259 'fn' : taskfn,
2260 'task' : task,
2261 'taskname' : taskname,
2262 'taskhash' : self.rqdata.get_task_hash(task),
2263 'unihash' : self.rqdata.get_task_unihash(task),
2264 'quieterrors' : True,
2265 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002266 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002267 'taskdepdata' : self.sq_build_taskdepdata(task),
2268 'dry_run' : False,
2269 'taskdep': taskdep,
2270 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2271 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2272 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2273 }
2274
Brad Bishop96ff1982019-08-19 13:50:42 -04002275 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
2276 if not mc in self.rq.fakeworker:
2277 self.rq.start_fakeworker(self, mc)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002278 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, runtask, "runtask")
Brad Bishop96ff1982019-08-19 13:50:42 -04002279 self.rq.fakeworker[mc].process.stdin.flush()
2280 else:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002281 RunQueue.send_pickled_data(self.rq.worker[mc].process, runtask, "runtask")
Brad Bishop96ff1982019-08-19 13:50:42 -04002282 self.rq.worker[mc].process.stdin.flush()
2283
Andrew Geissler517393d2023-01-13 08:55:19 -06002284 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002285 self.build_stamps2.append(self.build_stamps[task])
2286 self.sq_running.add(task)
2287 self.sq_live.add(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05002288 self.stats.updateActiveSetscene(len(self.sq_live))
Brad Bishop96ff1982019-08-19 13:50:42 -04002289 if self.can_start_task():
2290 return True
2291
Brad Bishopc68388fc2019-08-26 01:33:31 -04002292 self.update_holdofftasks()
2293
Brad Bishop08902b02019-08-20 09:16:51 -04002294 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 -05002295 hashequiv_logger.verbose("Setscene tasks completed")
Brad Bishop96ff1982019-08-19 13:50:42 -04002296
Brad Bishop08902b02019-08-20 09:16:51 -04002297 err = self.summarise_scenequeue_errors()
Brad Bishop96ff1982019-08-19 13:50:42 -04002298 if err:
2299 self.rq.state = runQueueFailed
2300 return True
2301
2302 if self.cooker.configuration.setsceneonly:
2303 self.rq.state = runQueueComplete
2304 return True
2305 self.sqdone = True
2306
2307 if self.stats.total == 0:
2308 # nothing to do
2309 self.rq.state = runQueueComplete
2310 return True
2311
2312 if self.cooker.configuration.setsceneonly:
2313 task = None
2314 else:
2315 task = self.sched.next()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002316 if task is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002317 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002318
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002319 if self.rqdata.setscene_ignore_tasks is not None:
2320 if self.check_setscene_ignore_tasks(task):
2321 self.task_fail(task, "setscene ignore_tasks")
Brad Bishop96ff1982019-08-19 13:50:42 -04002322 return True
2323
2324 if task in self.tasks_covered:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002325 logger.debug2("Setscene covered task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002326 self.task_skip(task, "covered")
2327 return True
2328
2329 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002330 logger.debug2("Stamp current task %s", task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002331
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002332 self.task_skip(task, "existing")
Andrew Geissler82c905d2020-04-13 13:39:40 -05002333 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002334 return True
2335
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002336 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002337 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2338 startevent = runQueueTaskStarted(task, self.stats, self.rq,
2339 noexec=True)
2340 bb.event.fire(startevent, self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002341 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002342 self.stats.taskActive()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002343 if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Andrew Geissler517393d2023-01-13 08:55:19 -06002344 bb.build.make_stamp_mcfn(taskname, taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002345 self.task_complete(task)
2346 return True
2347 else:
2348 startevent = runQueueTaskStarted(task, self.stats, self.rq)
2349 bb.event.fire(startevent, self.cfgData)
2350
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002351 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williams520786c2023-06-25 16:20:36 -05002352 realfn = bb.cache.virtualfn2realfn(taskfn)[0]
Andrew Geissler517393d2023-01-13 08:55:19 -06002353 runtask = {
2354 'fn' : taskfn,
2355 'task' : task,
2356 'taskname' : taskname,
2357 'taskhash' : self.rqdata.get_task_hash(task),
2358 'unihash' : self.rqdata.get_task_unihash(task),
2359 'quieterrors' : False,
2360 'appends' : self.cooker.collections[mc].get_file_appends(taskfn),
Patrick Williams520786c2023-06-25 16:20:36 -05002361 'layername' : self.cooker.collections[mc].calc_bbfile_priority(realfn)[2],
Andrew Geissler517393d2023-01-13 08:55:19 -06002362 'taskdepdata' : self.build_taskdepdata(task),
2363 'dry_run' : self.rqdata.setscene_enforce,
2364 'taskdep': taskdep,
2365 'fakerootenv' : self.rqdata.dataCaches[mc].fakerootenv[taskfn],
2366 'fakerootdirs' : self.rqdata.dataCaches[mc].fakerootdirs[taskfn],
2367 'fakerootnoenv' : self.rqdata.dataCaches[mc].fakerootnoenv[taskfn]
2368 }
2369
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002370 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 -05002371 if not mc in self.rq.fakeworker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002372 try:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002373 self.rq.start_fakeworker(self, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002374 except OSError as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002375 logger.critical("Failed to spawn fakeroot worker to run %s: %s" % (task, str(exc)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002376 self.rq.state = runQueueFailed
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002377 self.stats.taskFailed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002378 return True
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002379 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, runtask, "runtask")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002380 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002381 else:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002382 RunQueue.send_pickled_data(self.rq.worker[mc].process, runtask, "runtask")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002383 self.rq.worker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002384
Andrew Geissler517393d2023-01-13 08:55:19 -06002385 self.build_stamps[task] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002386 self.build_stamps2.append(self.build_stamps[task])
2387 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002388 self.stats.taskActive()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002389 if self.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002390 return True
2391
Andrew Geissler595f6302022-01-24 19:11:47 +00002392 if self.stats.active > 0 or self.sq_live:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002393 self.rq.read_workers()
2394 return self.rq.active_fds()
2395
Brad Bishop96ff1982019-08-19 13:50:42 -04002396 # No more tasks can be run. If we have deferred setscene tasks we should run them.
2397 if self.sq_deferred:
Patrick Williams92b42cb2022-09-03 06:53:57 -05002398 deferred_tid = list(self.sq_deferred.keys())[0]
2399 blocking_tid = self.sq_deferred.pop(deferred_tid)
Patrick Williams2390b1b2022-11-03 13:47:49 -05002400 logger.warning("Runqueue deadlocked on deferred tasks, forcing task %s blocked by %s" % (deferred_tid, blocking_tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04002401 return True
2402
Andrew Geissler595f6302022-01-24 19:11:47 +00002403 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002404 self.rq.state = runQueueFailed
2405 return True
2406
2407 # Sanity Checks
Brad Bishop08902b02019-08-20 09:16:51 -04002408 err = self.summarise_scenequeue_errors()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002409 for task in self.rqdata.runtaskentries:
2410 if task not in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002411 logger.error("Task %s never buildable!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002412 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002413 elif task not in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002414 logger.error("Task %s never ran!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002415 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002416 elif task not in self.runq_complete:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002417 logger.error("Task %s never completed!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002418 err = True
2419
2420 if err:
2421 self.rq.state = runQueueFailed
2422 else:
2423 self.rq.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002424
2425 return True
2426
Brad Bishopc68388fc2019-08-26 01:33:31 -04002427 def filtermcdeps(self, task, mc, deps):
Andrew Geissler99467da2019-02-25 18:54:23 -06002428 ret = set()
Andrew Geissler99467da2019-02-25 18:54:23 -06002429 for dep in deps:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002430 thismc = mc_from_tid(dep)
2431 if thismc != mc:
Andrew Geissler99467da2019-02-25 18:54:23 -06002432 continue
2433 ret.add(dep)
2434 return ret
2435
Patrick Williams73bd93f2024-02-20 08:07:48 -06002436 # Build the individual cache entries in advance once to save time
2437 def build_taskdepdata_cache(self):
2438 taskdepdata_cache = {}
2439 for task in self.rqdata.runtaskentries:
2440 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
2441 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2442 deps = self.rqdata.runtaskentries[task].depends
2443 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2444 taskhash = self.rqdata.runtaskentries[task].hash
2445 unihash = self.rqdata.runtaskentries[task].unihash
2446 deps = self.filtermcdeps(task, mc, deps)
2447 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2448 taskdepdata_cache[task] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
2449
2450 self.taskdepdata_cache = taskdepdata_cache
2451
Brad Bishopa34c0302019-09-23 22:34:48 -04002452 # We filter out multiconfig dependencies from taskdepdata we pass to the tasks
Andrew Geissler99467da2019-02-25 18:54:23 -06002453 # as most code can't handle them
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002454 def build_taskdepdata(self, task):
2455 taskdepdata = {}
Brad Bishopc68388fc2019-08-26 01:33:31 -04002456 mc = mc_from_tid(task)
Brad Bishop08902b02019-08-20 09:16:51 -04002457 next = self.rqdata.runtaskentries[task].depends.copy()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002458 next.add(task)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002459 next = self.filtermcdeps(task, mc, next)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002460 while next:
2461 additional = []
2462 for revdep in next:
Patrick Williams73bd93f2024-02-20 08:07:48 -06002463 self.taskdepdata_cache[revdep][6] = self.rqdata.runtaskentries[revdep].unihash
2464 taskdepdata[revdep] = self.taskdepdata_cache[revdep]
2465 for revdep2 in self.taskdepdata_cache[revdep][3]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002466 if revdep2 not in taskdepdata:
2467 additional.append(revdep2)
2468 next = additional
2469
2470 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2471 return taskdepdata
2472
Brad Bishop08902b02019-08-20 09:16:51 -04002473 def update_holdofftasks(self):
Brad Bishopc68388fc2019-08-26 01:33:31 -04002474
2475 if not self.holdoff_need_update:
2476 return
2477
2478 notcovered = set(self.scenequeue_notcovered)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002479 notcovered |= self.sqdata.cantskip
Brad Bishopc68388fc2019-08-26 01:33:31 -04002480 for tid in self.scenequeue_notcovered:
2481 notcovered |= self.sqdata.sq_covered_tasks[tid]
2482 notcovered |= self.sqdata.unskippable.difference(self.rqdata.runq_setscene_tids)
2483 notcovered.intersection_update(self.tasks_scenequeue_done)
2484
2485 covered = set(self.scenequeue_covered)
2486 for tid in self.scenequeue_covered:
2487 covered |= self.sqdata.sq_covered_tasks[tid]
2488 covered.difference_update(notcovered)
2489 covered.intersection_update(self.tasks_scenequeue_done)
2490
2491 for tid in notcovered | covered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002492 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002493 self.setbuildable(tid)
2494 elif self.rqdata.runtaskentries[tid].depends.issubset(self.runq_complete):
2495 self.setbuildable(tid)
2496
2497 self.tasks_covered = covered
2498 self.tasks_notcovered = notcovered
2499
Brad Bishop08902b02019-08-20 09:16:51 -04002500 self.holdoff_tasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002501
Brad Bishop08902b02019-08-20 09:16:51 -04002502 for tid in self.rqdata.runq_setscene_tids:
2503 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2504 self.holdoff_tasks.add(tid)
2505
2506 for tid in self.holdoff_tasks.copy():
2507 for dep in self.sqdata.sq_covered_tasks[tid]:
2508 if dep not in self.runq_complete:
2509 self.holdoff_tasks.add(dep)
2510
Brad Bishopc68388fc2019-08-26 01:33:31 -04002511 self.holdoff_need_update = False
2512
Brad Bishop08902b02019-08-20 09:16:51 -04002513 def process_possible_migrations(self):
2514
2515 changed = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002516 toprocess = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002517 for tid, unihash in self.updated_taskhash_queue.copy():
2518 if tid in self.runq_running and tid not in self.runq_complete:
2519 continue
2520
2521 self.updated_taskhash_queue.remove((tid, unihash))
2522
2523 if unihash != self.rqdata.runtaskentries[tid].unihash:
Andrew Geisslerc926e172021-05-07 16:11:35 -05002524 # Make sure we rehash any other tasks with the same task hash that we're deferred against.
2525 torehash = [tid]
2526 for deftid in self.sq_deferred:
2527 if self.sq_deferred[deftid] == tid:
2528 torehash.append(deftid)
2529 for hashtid in torehash:
2530 hashequiv_logger.verbose("Task %s unihash changed to %s" % (hashtid, unihash))
2531 self.rqdata.runtaskentries[hashtid].unihash = unihash
2532 bb.parse.siggen.set_unihash(hashtid, unihash)
2533 toprocess.add(hashtid)
Andrew Geissler78b72792022-06-14 06:47:25 -05002534 if torehash:
2535 # Need to save after set_unihash above
2536 bb.parse.siggen.save_unitaskhashes()
Brad Bishop08902b02019-08-20 09:16:51 -04002537
Andrew Geissler82c905d2020-04-13 13:39:40 -05002538 # Work out all tasks which depend upon these
2539 total = set()
2540 next = set()
2541 for p in toprocess:
2542 next |= self.rqdata.runtaskentries[p].revdeps
2543 while next:
2544 current = next.copy()
2545 total = total | next
2546 next = set()
2547 for ntid in current:
2548 next |= self.rqdata.runtaskentries[ntid].revdeps
2549 next.difference_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002550
Andrew Geissler82c905d2020-04-13 13:39:40 -05002551 # Now iterate those tasks in dependency order to regenerate their taskhash/unihash
2552 next = set()
2553 for p in total:
Andrew Geissler595f6302022-01-24 19:11:47 +00002554 if not self.rqdata.runtaskentries[p].depends:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002555 next.add(p)
2556 elif self.rqdata.runtaskentries[p].depends.isdisjoint(total):
2557 next.add(p)
2558
2559 # When an item doesn't have dependencies in total, we can process it. Drop items from total when handled
2560 while next:
2561 current = next.copy()
2562 next = set()
2563 for tid in current:
Andrew Geissler595f6302022-01-24 19:11:47 +00002564 if self.rqdata.runtaskentries[p].depends and not self.rqdata.runtaskentries[tid].depends.isdisjoint(total):
Andrew Geissler82c905d2020-04-13 13:39:40 -05002565 continue
2566 orighash = self.rqdata.runtaskentries[tid].hash
Andrew Geissler517393d2023-01-13 08:55:19 -06002567 newhash = bb.parse.siggen.get_taskhash(tid, self.rqdata.runtaskentries[tid].depends, self.rqdata.dataCaches)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002568 origuni = self.rqdata.runtaskentries[tid].unihash
2569 newuni = bb.parse.siggen.get_unihash(tid)
2570 # FIXME, need to check it can come from sstate at all for determinism?
2571 remapped = False
2572 if newuni == origuni:
2573 # Nothing to do, we match, skip code below
2574 remapped = True
2575 elif tid in self.scenequeue_covered or tid in self.sq_live:
2576 # Already ran this setscene task or it running. Report the new taskhash
2577 bb.parse.siggen.report_unihash_equiv(tid, newhash, origuni, newuni, self.rqdata.dataCaches)
2578 hashequiv_logger.verbose("Already covered setscene for %s so ignoring rehash (remap)" % (tid))
2579 remapped = True
2580
2581 if not remapped:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002582 #logger.debug("Task %s hash changes: %s->%s %s->%s" % (tid, orighash, newhash, origuni, newuni))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002583 self.rqdata.runtaskentries[tid].hash = newhash
2584 self.rqdata.runtaskentries[tid].unihash = newuni
2585 changed.add(tid)
2586
2587 next |= self.rqdata.runtaskentries[tid].revdeps
2588 total.remove(tid)
2589 next.intersection_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002590
2591 if changed:
2592 for mc in self.rq.worker:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002593 RunQueue.send_pickled_data(self.rq.worker[mc].process, bb.parse.siggen.get_taskhashes(), "newtaskhashes")
Brad Bishop08902b02019-08-20 09:16:51 -04002594 for mc in self.rq.fakeworker:
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002595 RunQueue.send_pickled_data(self.rq.fakeworker[mc].process, bb.parse.siggen.get_taskhashes(), "newtaskhashes")
Brad Bishop08902b02019-08-20 09:16:51 -04002596
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002597 hashequiv_logger.debug(pprint.pformat("Tasks changed:\n%s" % (changed)))
Brad Bishop08902b02019-08-20 09:16:51 -04002598
2599 for tid in changed:
2600 if tid not in self.rqdata.runq_setscene_tids:
2601 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002602 if tid not in self.pending_migrations:
2603 self.pending_migrations.add(tid)
2604
Andrew Geissler82c905d2020-04-13 13:39:40 -05002605 update_tasks = []
Brad Bishop08902b02019-08-20 09:16:51 -04002606 for tid in self.pending_migrations.copy():
Andrew Geissler82c905d2020-04-13 13:39:40 -05002607 if tid in self.runq_running or tid in self.sq_live:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002608 # Too late, task already running, not much we can do now
2609 self.pending_migrations.remove(tid)
2610 continue
2611
Brad Bishop08902b02019-08-20 09:16:51 -04002612 valid = True
2613 # Check no tasks this covers are running
2614 for dep in self.sqdata.sq_covered_tasks[tid]:
2615 if dep in self.runq_running and dep not in self.runq_complete:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002616 hashequiv_logger.debug2("Task %s is running which blocks setscene for %s from running" % (dep, tid))
Brad Bishop08902b02019-08-20 09:16:51 -04002617 valid = False
2618 break
2619 if not valid:
2620 continue
2621
2622 self.pending_migrations.remove(tid)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002623 changed = True
Brad Bishop08902b02019-08-20 09:16:51 -04002624
2625 if tid in self.tasks_scenequeue_done:
2626 self.tasks_scenequeue_done.remove(tid)
2627 for dep in self.sqdata.sq_covered_tasks[tid]:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002628 if dep in self.runq_complete and dep not in self.runq_tasksrun:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002629 bb.error("Task %s marked as completed but now needing to rerun? Halting build." % dep)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002630 self.failed_tids.append(tid)
2631 self.rq.state = runQueueCleanUp
2632 return
2633
Brad Bishop08902b02019-08-20 09:16:51 -04002634 if dep not in self.runq_complete:
2635 if dep in self.tasks_scenequeue_done and dep not in self.sqdata.unskippable:
2636 self.tasks_scenequeue_done.remove(dep)
2637
2638 if tid in self.sq_buildable:
2639 self.sq_buildable.remove(tid)
2640 if tid in self.sq_running:
2641 self.sq_running.remove(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06002642 if tid in self.sqdata.outrightfail:
2643 self.sqdata.outrightfail.remove(tid)
2644 if tid in self.scenequeue_notcovered:
2645 self.scenequeue_notcovered.remove(tid)
2646 if tid in self.scenequeue_covered:
2647 self.scenequeue_covered.remove(tid)
2648 if tid in self.scenequeue_notneeded:
2649 self.scenequeue_notneeded.remove(tid)
2650
2651 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2652 self.sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2653
2654 if tid in self.stampcache:
2655 del self.stampcache[tid]
2656
2657 if tid in self.build_stamps:
2658 del self.build_stamps[tid]
2659
2660 update_tasks.append(tid)
2661
2662 update_tasks2 = []
2663 for tid in update_tasks:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002664 harddepfail = False
Patrick Williams169d7bc2024-01-05 11:33:25 -06002665 for t in self.sqdata.sq_harddeps_rev[tid]:
2666 if t in self.scenequeue_notcovered:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002667 harddepfail = True
2668 break
2669 if not harddepfail and self.sqdata.sq_revdeps[tid].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
Brad Bishop08902b02019-08-20 09:16:51 -04002670 if tid not in self.sq_buildable:
2671 self.sq_buildable.add(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +00002672 if not self.sqdata.sq_revdeps[tid]:
Brad Bishop08902b02019-08-20 09:16:51 -04002673 self.sq_buildable.add(tid)
2674
Andrew Geissler517393d2023-01-13 08:55:19 -06002675 update_tasks2.append((tid, harddepfail, tid in self.sqdata.valid))
Brad Bishop08902b02019-08-20 09:16:51 -04002676
Andrew Geissler517393d2023-01-13 08:55:19 -06002677 if update_tasks2:
Brad Bishop08902b02019-08-20 09:16:51 -04002678 self.sqdone = False
Patrick Williams92b42cb2022-09-03 06:53:57 -05002679 for mc in sorted(self.sqdata.multiconfigs):
Andrew Geissler517393d2023-01-13 08:55:19 -06002680 for tid in sorted([t[0] for t in update_tasks2]):
Patrick Williams92b42cb2022-09-03 06:53:57 -05002681 if mc_from_tid(tid) != mc:
2682 continue
2683 h = pending_hash_index(tid, self.rqdata)
2684 if h in self.sqdata.hashes and tid != self.sqdata.hashes[h]:
2685 self.sq_deferred[tid] = self.sqdata.hashes[h]
2686 bb.note("Deferring %s after %s" % (tid, self.sqdata.hashes[h]))
Andrew Geissler517393d2023-01-13 08:55:19 -06002687 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 -05002688
Andrew Geissler517393d2023-01-13 08:55:19 -06002689 for (tid, harddepfail, origvalid) in update_tasks2:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002690 if tid in self.sqdata.valid and not origvalid:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002691 hashequiv_logger.verbose("Setscene task %s became valid" % tid)
2692 if harddepfail:
Andrew Geissler517393d2023-01-13 08:55:19 -06002693 logger.debug2("%s has an unavailable hard dependency so skipping" % (tid))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002694 self.sq_task_failoutright(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002695
2696 if changed:
Andrew Geissler5199d832021-09-24 16:47:35 -05002697 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Patrick Williams169d7bc2024-01-05 11:33:25 -06002698 self.sq_needed_harddeps = set()
Patrick Williams73bd93f2024-02-20 08:07:48 -06002699 self.sq_harddep_deferred = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04002700 self.holdoff_need_update = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002701
Brad Bishop96ff1982019-08-19 13:50:42 -04002702 def scenequeue_updatecounters(self, task, fail=False):
Brad Bishop08902b02019-08-20 09:16:51 -04002703
Patrick Williams169d7bc2024-01-05 11:33:25 -06002704 if fail and task in self.sqdata.sq_harddeps:
2705 for dep in sorted(self.sqdata.sq_harddeps[task]):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002706 if dep in self.scenequeue_covered or dep in self.scenequeue_notcovered:
2707 # dependency could be already processed, e.g. noexec setscene task
2708 continue
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002709 noexec, stamppresent = check_setscene_stamps(dep, self.rqdata, self.rq, self.stampcache)
2710 if noexec or stamppresent:
2711 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002712 logger.debug2("%s was unavailable and is a hard dependency of %s so skipping" % (task, dep))
Brad Bishop96ff1982019-08-19 13:50:42 -04002713 self.sq_task_failoutright(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002714 continue
Patrick Williams169d7bc2024-01-05 11:33:25 -06002715 for dep in sorted(self.sqdata.sq_deps[task]):
Brad Bishop08902b02019-08-20 09:16:51 -04002716 if self.sqdata.sq_revdeps[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2717 if dep not in self.sq_buildable:
2718 self.sq_buildable.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002719
Brad Bishop96ff1982019-08-19 13:50:42 -04002720 next = set([task])
2721 while next:
2722 new = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002723 for t in sorted(next):
Brad Bishop96ff1982019-08-19 13:50:42 -04002724 self.tasks_scenequeue_done.add(t)
2725 # Look down the dependency chain for non-setscene things which this task depends on
2726 # and mark as 'done'
2727 for dep in self.rqdata.runtaskentries[t].depends:
2728 if dep in self.rqdata.runq_setscene_tids or dep in self.tasks_scenequeue_done:
2729 continue
2730 if self.rqdata.runtaskentries[dep].revdeps.issubset(self.tasks_scenequeue_done):
2731 new.add(dep)
Brad Bishop96ff1982019-08-19 13:50:42 -04002732 next = new
2733
Patrick Williams73bd93f2024-02-20 08:07:48 -06002734 # If this task was one which other setscene tasks have a hard dependency upon, we need
2735 # to walk through the hard dependencies and allow execution of those which have completed dependencies.
2736 if task in self.sqdata.sq_harddeps:
2737 for dep in self.sq_harddep_deferred.copy():
2738 if self.sqdata.sq_harddeps_rev[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2739 self.sq_harddep_deferred.remove(dep)
2740
Andrew Geissler5199d832021-09-24 16:47:35 -05002741 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002742 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002743
2744 def sq_task_completeoutright(self, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002745 """
2746 Mark a task as completed
2747 Look at the reverse dependencies and mark any task with
2748 completed dependencies as buildable
2749 """
2750
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002751 logger.debug('Found task %s which could be accelerated', task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002752 self.scenequeue_covered.add(task)
2753 self.scenequeue_updatecounters(task)
2754
Brad Bishop96ff1982019-08-19 13:50:42 -04002755 def sq_check_taskfail(self, task):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002756 if self.rqdata.setscene_ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002757 realtask = task.split('_setscene')[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002758 (mc, fn, taskname, taskfn) = split_tid_mcfn(realtask)
2759 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002760 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002761 logger.error('Task %s.%s failed' % (pn, taskname + "_setscene"))
2762 self.rq.state = runQueueCleanUp
2763
Brad Bishop96ff1982019-08-19 13:50:42 -04002764 def sq_task_complete(self, task):
Andrew Geissler5199d832021-09-24 16:47:35 -05002765 bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
Brad Bishop96ff1982019-08-19 13:50:42 -04002766 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002767
Brad Bishop96ff1982019-08-19 13:50:42 -04002768 def sq_task_fail(self, task, result):
Andrew Geissler5199d832021-09-24 16:47:35 -05002769 bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002770 self.scenequeue_notcovered.add(task)
2771 self.scenequeue_updatecounters(task, True)
Brad Bishop96ff1982019-08-19 13:50:42 -04002772 self.sq_check_taskfail(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002773
Brad Bishop96ff1982019-08-19 13:50:42 -04002774 def sq_task_failoutright(self, task):
2775 self.sq_running.add(task)
2776 self.sq_buildable.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002777 self.scenequeue_notcovered.add(task)
2778 self.scenequeue_updatecounters(task, True)
2779
Brad Bishop96ff1982019-08-19 13:50:42 -04002780 def sq_task_skip(self, task):
2781 self.sq_running.add(task)
2782 self.sq_buildable.add(task)
2783 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002784
Brad Bishop96ff1982019-08-19 13:50:42 -04002785 def sq_build_taskdepdata(self, task):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002786 def getsetscenedeps(tid):
2787 deps = set()
2788 (mc, fn, taskname, _) = split_tid_mcfn(tid)
2789 realtid = tid + "_setscene"
2790 idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
2791 for (depname, idependtask) in idepends:
2792 if depname not in self.rqdata.taskData[mc].build_targets:
2793 continue
2794
2795 depfn = self.rqdata.taskData[mc].build_targets[depname][0]
2796 if depfn is None:
2797 continue
2798 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2799 deps.add(deptid)
2800 return deps
2801
2802 taskdepdata = {}
2803 next = getsetscenedeps(task)
2804 next.add(task)
2805 while next:
2806 additional = []
2807 for revdep in next:
2808 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2809 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2810 deps = getsetscenedeps(revdep)
2811 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2812 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002813 unihash = self.rqdata.runtaskentries[revdep].unihash
Patrick Williamsb542dec2023-06-09 01:26:37 -05002814 hashfn = self.rqdata.dataCaches[mc].hashfn[taskfn]
2815 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash, hashfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002816 for revdep2 in deps:
2817 if revdep2 not in taskdepdata:
2818 additional.append(revdep2)
2819 next = additional
2820
2821 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2822 return taskdepdata
2823
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002824 def check_setscene_ignore_tasks(self, tid):
2825 # Check task that is going to run against the ignore tasks list
Brad Bishop96ff1982019-08-19 13:50:42 -04002826 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2827 # Ignore covered tasks
2828 if tid in self.tasks_covered:
2829 return False
2830 # Ignore stamped tasks
2831 if self.rq.check_stamp_task(tid, taskname, cache=self.stampcache):
2832 return False
2833 # Ignore noexec tasks
2834 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2835 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2836 return False
2837
2838 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002839 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Brad Bishop96ff1982019-08-19 13:50:42 -04002840 if tid in self.rqdata.runq_setscene_tids:
Andrew Geissler595f6302022-01-24 19:11:47 +00002841 msg = ['Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)]
Brad Bishop96ff1982019-08-19 13:50:42 -04002842 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002843 msg = ['Task %s.%s attempted to execute unexpectedly' % (pn, taskname)]
Andrew Geissler82c905d2020-04-13 13:39:40 -05002844 for t in self.scenequeue_notcovered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002845 msg.append("\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash))
2846 msg.append('\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered))
2847 logger.error("".join(msg))
Brad Bishop96ff1982019-08-19 13:50:42 -04002848 return True
2849 return False
2850
2851class SQData(object):
2852 def __init__(self):
2853 # SceneQueue dependencies
2854 self.sq_deps = {}
2855 # SceneQueue reverse dependencies
2856 self.sq_revdeps = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002857 # Injected inter-setscene task dependencies
2858 self.sq_harddeps = {}
Patrick Williams169d7bc2024-01-05 11:33:25 -06002859 self.sq_harddeps_rev = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002860 # Cache of stamp files so duplicates can't run in parallel
2861 self.stamps = {}
2862 # Setscene tasks directly depended upon by the build
2863 self.unskippable = set()
2864 # List of setscene tasks which aren't present
2865 self.outrightfail = set()
2866 # A list of normal tasks a setscene task covers
2867 self.sq_covered_tasks = {}
2868
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002869def build_scenequeue_data(sqdata, rqdata, sqrq):
Brad Bishop96ff1982019-08-19 13:50:42 -04002870
2871 sq_revdeps = {}
2872 sq_revdeps_squash = {}
2873 sq_collated_deps = {}
2874
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002875 # We can't skip specified target tasks which aren't setscene tasks
2876 sqdata.cantskip = set(rqdata.target_tids)
2877 sqdata.cantskip.difference_update(rqdata.runq_setscene_tids)
2878 sqdata.cantskip.intersection_update(rqdata.runtaskentries)
2879
Brad Bishop96ff1982019-08-19 13:50:42 -04002880 # We need to construct a dependency graph for the setscene functions. Intermediate
2881 # dependencies between the setscene tasks only complicate the code. This code
2882 # therefore aims to collapse the huge runqueue dependency tree into a smaller one
2883 # only containing the setscene functions.
2884
2885 rqdata.init_progress_reporter.next_stage()
2886
2887 # First process the chains up to the first setscene task.
2888 endpoints = {}
2889 for tid in rqdata.runtaskentries:
2890 sq_revdeps[tid] = copy.copy(rqdata.runtaskentries[tid].revdeps)
2891 sq_revdeps_squash[tid] = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00002892 if not sq_revdeps[tid] and tid not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002893 #bb.warn("Added endpoint %s" % (tid))
2894 endpoints[tid] = set()
2895
2896 rqdata.init_progress_reporter.next_stage()
2897
2898 # Secondly process the chains between setscene tasks.
2899 for tid in rqdata.runq_setscene_tids:
2900 sq_collated_deps[tid] = set()
2901 #bb.warn("Added endpoint 2 %s" % (tid))
2902 for dep in rqdata.runtaskentries[tid].depends:
2903 if tid in sq_revdeps[dep]:
2904 sq_revdeps[dep].remove(tid)
2905 if dep not in endpoints:
2906 endpoints[dep] = set()
2907 #bb.warn(" Added endpoint 3 %s" % (dep))
2908 endpoints[dep].add(tid)
2909
2910 rqdata.init_progress_reporter.next_stage()
2911
2912 def process_endpoints(endpoints):
2913 newendpoints = {}
2914 for point, task in endpoints.items():
2915 tasks = set()
2916 if task:
2917 tasks |= task
2918 if sq_revdeps_squash[point]:
2919 tasks |= sq_revdeps_squash[point]
2920 if point not in rqdata.runq_setscene_tids:
2921 for t in tasks:
2922 sq_collated_deps[t].add(point)
2923 sq_revdeps_squash[point] = set()
2924 if point in rqdata.runq_setscene_tids:
2925 sq_revdeps_squash[point] = tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002926 continue
2927 for dep in rqdata.runtaskentries[point].depends:
2928 if point in sq_revdeps[dep]:
2929 sq_revdeps[dep].remove(point)
2930 if tasks:
2931 sq_revdeps_squash[dep] |= tasks
Andrew Geissler595f6302022-01-24 19:11:47 +00002932 if not sq_revdeps[dep] and dep not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002933 newendpoints[dep] = task
Andrew Geissler595f6302022-01-24 19:11:47 +00002934 if newendpoints:
Brad Bishop96ff1982019-08-19 13:50:42 -04002935 process_endpoints(newendpoints)
2936
2937 process_endpoints(endpoints)
2938
2939 rqdata.init_progress_reporter.next_stage()
2940
Brad Bishop08902b02019-08-20 09:16:51 -04002941 # Build a list of tasks which are "unskippable"
2942 # These are direct endpoints referenced by the build upto and including setscene tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002943 # Take the build endpoints (no revdeps) and find the sstate tasks they depend upon
2944 new = True
2945 for tid in rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00002946 if not rqdata.runtaskentries[tid].revdeps:
Brad Bishop96ff1982019-08-19 13:50:42 -04002947 sqdata.unskippable.add(tid)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06002948 sqdata.unskippable |= sqdata.cantskip
Brad Bishop96ff1982019-08-19 13:50:42 -04002949 while new:
2950 new = False
Brad Bishop08902b02019-08-20 09:16:51 -04002951 orig = sqdata.unskippable.copy()
2952 for tid in sorted(orig, reverse=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04002953 if tid in rqdata.runq_setscene_tids:
2954 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00002955 if not rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04002956 # These are tasks which have no setscene tasks in their chain, need to mark as directly buildable
Brad Bishop96ff1982019-08-19 13:50:42 -04002957 sqrq.setbuildable(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002958 sqdata.unskippable |= rqdata.runtaskentries[tid].depends
Brad Bishop08902b02019-08-20 09:16:51 -04002959 if sqdata.unskippable != orig:
2960 new = True
2961
2962 sqrq.tasks_scenequeue_done |= sqdata.unskippable.difference(rqdata.runq_setscene_tids)
Brad Bishop96ff1982019-08-19 13:50:42 -04002963
2964 rqdata.init_progress_reporter.next_stage(len(rqdata.runtaskentries))
2965
2966 # Sanity check all dependencies could be changed to setscene task references
2967 for taskcounter, tid in enumerate(rqdata.runtaskentries):
2968 if tid in rqdata.runq_setscene_tids:
2969 pass
Andrew Geissler595f6302022-01-24 19:11:47 +00002970 elif sq_revdeps_squash[tid]:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002971 bb.msg.fatal("RunQueue", "Something went badly wrong during scenequeue generation, halting. Please report this problem.")
Brad Bishop96ff1982019-08-19 13:50:42 -04002972 else:
2973 del sq_revdeps_squash[tid]
2974 rqdata.init_progress_reporter.update(taskcounter)
2975
2976 rqdata.init_progress_reporter.next_stage()
2977
2978 # Resolve setscene inter-task dependencies
2979 # e.g. do_sometask_setscene[depends] = "targetname:do_someothertask_setscene"
2980 # Note that anything explicitly depended upon will have its reverse dependencies removed to avoid circular dependencies
2981 for tid in rqdata.runq_setscene_tids:
2982 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2983 realtid = tid + "_setscene"
2984 idepends = rqdata.taskData[mc].taskentries[realtid].idepends
Andrew Geissler517393d2023-01-13 08:55:19 -06002985 sqdata.stamps[tid] = bb.parse.siggen.stampfile_mcfn(taskname, taskfn, extrainfo=False)
2986
Patrick Williams169d7bc2024-01-05 11:33:25 -06002987 sqdata.sq_harddeps_rev[tid] = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002988 for (depname, idependtask) in idepends:
2989
2990 if depname not in rqdata.taskData[mc].build_targets:
2991 continue
2992
2993 depfn = rqdata.taskData[mc].build_targets[depname][0]
2994 if depfn is None:
2995 continue
2996 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2997 if deptid not in rqdata.runtaskentries:
2998 bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (realtid, depfn, idependtask))
2999
Patrick Williams169d7bc2024-01-05 11:33:25 -06003000 logger.debug2("Adding hard setscene dependency %s for %s" % (deptid, tid))
3001
Brad Bishop96ff1982019-08-19 13:50:42 -04003002 if not deptid in sqdata.sq_harddeps:
3003 sqdata.sq_harddeps[deptid] = set()
3004 sqdata.sq_harddeps[deptid].add(tid)
Patrick Williams169d7bc2024-01-05 11:33:25 -06003005 sqdata.sq_harddeps_rev[tid].add(deptid)
Brad Bishop96ff1982019-08-19 13:50:42 -04003006
3007 rqdata.init_progress_reporter.next_stage()
3008
Brad Bishop96ff1982019-08-19 13:50:42 -04003009 rqdata.init_progress_reporter.next_stage()
3010
3011 #for tid in sq_revdeps_squash:
3012 # data = ""
3013 # for dep in sq_revdeps_squash[tid]:
3014 # data = data + "\n %s" % dep
3015 # bb.warn("Task %s_setscene: is %s " % (tid, data))
3016
3017 sqdata.sq_revdeps = sq_revdeps_squash
Brad Bishop96ff1982019-08-19 13:50:42 -04003018 sqdata.sq_covered_tasks = sq_collated_deps
3019
3020 # Build reverse version of revdeps to populate deps structure
3021 for tid in sqdata.sq_revdeps:
3022 sqdata.sq_deps[tid] = set()
3023 for tid in sqdata.sq_revdeps:
3024 for dep in sqdata.sq_revdeps[tid]:
3025 sqdata.sq_deps[dep].add(tid)
3026
3027 rqdata.init_progress_reporter.next_stage()
3028
Brad Bishop00e122a2019-10-05 11:10:57 -04003029 sqdata.multiconfigs = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04003030 for tid in sqdata.sq_revdeps:
Brad Bishop00e122a2019-10-05 11:10:57 -04003031 sqdata.multiconfigs.add(mc_from_tid(tid))
Andrew Geissler595f6302022-01-24 19:11:47 +00003032 if not sqdata.sq_revdeps[tid]:
Brad Bishop96ff1982019-08-19 13:50:42 -04003033 sqrq.sq_buildable.add(tid)
3034
Patrick Williams169d7bc2024-01-05 11:33:25 -06003035 rqdata.init_progress_reporter.next_stage()
Brad Bishop96ff1982019-08-19 13:50:42 -04003036
Brad Bishop00e122a2019-10-05 11:10:57 -04003037 sqdata.noexec = set()
3038 sqdata.stamppresent = set()
3039 sqdata.valid = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04003040
Patrick Williams213cb262021-08-07 19:21:33 -05003041 sqdata.hashes = {}
3042 sqrq.sq_deferred = {}
3043 for mc in sorted(sqdata.multiconfigs):
3044 for tid in sorted(sqdata.sq_revdeps):
3045 if mc_from_tid(tid) != mc:
3046 continue
3047 h = pending_hash_index(tid, rqdata)
3048 if h not in sqdata.hashes:
3049 sqdata.hashes[h] = tid
3050 else:
3051 sqrq.sq_deferred[tid] = sqdata.hashes[h]
Andrew Geissler8f840682023-07-21 09:09:43 -05003052 bb.debug(1, "Deferring %s after %s" % (tid, sqdata.hashes[h]))
Patrick Williams213cb262021-08-07 19:21:33 -05003053
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003054def check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=False):
3055
3056 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
3057
3058 taskdep = rqdata.dataCaches[mc].task_deps[taskfn]
3059
3060 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Andrew Geissler517393d2023-01-13 08:55:19 -06003061 bb.build.make_stamp_mcfn(taskname + "_setscene", taskfn)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003062 return True, False
3063
3064 if rq.check_stamp_task(tid, taskname + "_setscene", cache=stampcache):
3065 logger.debug2('Setscene stamp current for task %s', tid)
3066 return False, True
3067
3068 if rq.check_stamp_task(tid, taskname, recurse = True, cache=stampcache):
3069 logger.debug2('Normal stamp current for task %s', tid)
3070 return False, True
3071
3072 return False, False
3073
Brad Bishop1d80a2e2019-11-15 16:35:03 -05003074def update_scenequeue_data(tids, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True):
Brad Bishop00e122a2019-10-05 11:10:57 -04003075
3076 tocheck = set()
3077
3078 for tid in sorted(tids):
3079 if tid in sqdata.stamppresent:
3080 sqdata.stamppresent.remove(tid)
3081 if tid in sqdata.valid:
3082 sqdata.valid.remove(tid)
Andrew Geisslerc926e172021-05-07 16:11:35 -05003083 if tid in sqdata.outrightfail:
3084 sqdata.outrightfail.remove(tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04003085
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003086 noexec, stamppresent = check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04003087
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003088 if noexec:
Brad Bishop00e122a2019-10-05 11:10:57 -04003089 sqdata.noexec.add(tid)
3090 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003091 logger.debug2("%s is noexec so skipping setscene" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04003092 continue
3093
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05003094 if stamppresent:
Brad Bishop00e122a2019-10-05 11:10:57 -04003095 sqdata.stamppresent.add(tid)
3096 sqrq.sq_task_skip(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003097 logger.debug2("%s has a valid stamp, skipping" % (tid))
Brad Bishop00e122a2019-10-05 11:10:57 -04003098 continue
3099
3100 tocheck.add(tid)
3101
Brad Bishop1d80a2e2019-11-15 16:35:03 -05003102 sqdata.valid |= rq.validate_hashes(tocheck, cooker.data, len(sqdata.stamppresent), False, summary=summary)
Brad Bishop00e122a2019-10-05 11:10:57 -04003103
Patrick Williams213cb262021-08-07 19:21:33 -05003104 for tid in tids:
3105 if tid in sqdata.stamppresent:
3106 continue
3107 if tid in sqdata.valid:
3108 continue
3109 if tid in sqdata.noexec:
3110 continue
3111 if tid in sqrq.scenequeue_covered:
3112 continue
3113 if tid in sqrq.scenequeue_notcovered:
3114 continue
3115 if tid in sqrq.sq_deferred:
3116 continue
3117 sqdata.outrightfail.add(tid)
Andrew Geissler517393d2023-01-13 08:55:19 -06003118 logger.debug2("%s already handled (fallthrough), skipping" % (tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04003119
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003120class TaskFailure(Exception):
3121 """
3122 Exception raised when a task in a runqueue fails
3123 """
3124 def __init__(self, x):
3125 self.args = x
3126
3127
3128class runQueueExitWait(bb.event.Event):
3129 """
3130 Event when waiting for task processes to exit
3131 """
3132
3133 def __init__(self, remain):
3134 self.remain = remain
3135 self.message = "Waiting for %s active tasks to finish" % remain
3136 bb.event.Event.__init__(self)
3137
3138class runQueueEvent(bb.event.Event):
3139 """
3140 Base runQueue event class
3141 """
3142 def __init__(self, task, stats, rq):
3143 self.taskid = task
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003144 self.taskstring = task
3145 self.taskname = taskname_from_tid(task)
3146 self.taskfile = fn_from_tid(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003147 self.taskhash = rq.rqdata.get_task_hash(task)
3148 self.stats = stats.copy()
3149 bb.event.Event.__init__(self)
3150
3151class sceneQueueEvent(runQueueEvent):
3152 """
3153 Base sceneQueue event class
3154 """
3155 def __init__(self, task, stats, rq, noexec=False):
3156 runQueueEvent.__init__(self, task, stats, rq)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003157 self.taskstring = task + "_setscene"
3158 self.taskname = taskname_from_tid(task) + "_setscene"
3159 self.taskfile = fn_from_tid(task)
3160 self.taskhash = rq.rqdata.get_task_hash(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003161
3162class runQueueTaskStarted(runQueueEvent):
3163 """
3164 Event notifying a task was started
3165 """
3166 def __init__(self, task, stats, rq, noexec=False):
3167 runQueueEvent.__init__(self, task, stats, rq)
3168 self.noexec = noexec
3169
3170class sceneQueueTaskStarted(sceneQueueEvent):
3171 """
3172 Event notifying a setscene task was started
3173 """
3174 def __init__(self, task, stats, rq, noexec=False):
3175 sceneQueueEvent.__init__(self, task, stats, rq)
3176 self.noexec = noexec
3177
3178class runQueueTaskFailed(runQueueEvent):
3179 """
3180 Event notifying a task failed
3181 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003182 def __init__(self, task, stats, exitcode, rq, fakeroot_log=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003183 runQueueEvent.__init__(self, task, stats, rq)
3184 self.exitcode = exitcode
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003185 self.fakeroot_log = fakeroot_log
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003186
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003187 def __str__(self):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003188 if self.fakeroot_log:
3189 return "Task (%s) failed with exit code '%s' \nPseudo log:\n%s" % (self.taskstring, self.exitcode, self.fakeroot_log)
3190 else:
3191 return "Task (%s) failed with exit code '%s'" % (self.taskstring, self.exitcode)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003192
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003193class sceneQueueTaskFailed(sceneQueueEvent):
3194 """
3195 Event notifying a setscene task failed
3196 """
3197 def __init__(self, task, stats, exitcode, rq):
3198 sceneQueueEvent.__init__(self, task, stats, rq)
3199 self.exitcode = exitcode
3200
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003201 def __str__(self):
3202 return "Setscene task (%s) failed with exit code '%s' - real task will be run instead" % (self.taskstring, self.exitcode)
3203
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003204class sceneQueueComplete(sceneQueueEvent):
3205 """
3206 Event when all the sceneQueue tasks are complete
3207 """
3208 def __init__(self, stats, rq):
3209 self.stats = stats.copy()
3210 bb.event.Event.__init__(self)
3211
3212class runQueueTaskCompleted(runQueueEvent):
3213 """
3214 Event notifying a task completed
3215 """
3216
3217class sceneQueueTaskCompleted(sceneQueueEvent):
3218 """
3219 Event notifying a setscene task completed
3220 """
3221
3222class runQueueTaskSkipped(runQueueEvent):
3223 """
3224 Event notifying a task was skipped
3225 """
3226 def __init__(self, task, stats, rq, reason):
3227 runQueueEvent.__init__(self, task, stats, rq)
3228 self.reason = reason
3229
Brad Bishop08902b02019-08-20 09:16:51 -04003230class taskUniHashUpdate(bb.event.Event):
3231 """
3232 Base runQueue event class
3233 """
3234 def __init__(self, task, unihash):
3235 self.taskid = task
3236 self.unihash = unihash
3237 bb.event.Event.__init__(self)
3238
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003239class runQueuePipe():
3240 """
3241 Abstraction for a pipe between a worker thread and the server
3242 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003243 def __init__(self, pipein, pipeout, d, rq, rqexec, fakerootlogs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003244 self.input = pipein
3245 if pipeout:
3246 pipeout.close()
3247 bb.utils.nonblockingfd(self.input)
Andrew Geissler220dafd2023-10-04 10:18:08 -05003248 self.queue = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003249 self.d = d
3250 self.rq = rq
3251 self.rqexec = rqexec
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003252 self.fakerootlogs = fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003253
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003254 def read(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003255 for workers, name in [(self.rq.worker, "Worker"), (self.rq.fakeworker, "Fakeroot")]:
3256 for worker in workers.values():
3257 worker.process.poll()
3258 if worker.process.returncode is not None and not self.rq.teardown:
3259 bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, worker.process.pid, str(worker.process.returncode)))
3260 self.rq.finish_runqueue(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003261
3262 start = len(self.queue)
3263 try:
Andrew Geissler220dafd2023-10-04 10:18:08 -05003264 self.queue.extend(self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003265 except (OSError, IOError) as e:
3266 if e.errno != errno.EAGAIN:
3267 raise
3268 end = len(self.queue)
3269 found = True
Andrew Geissler595f6302022-01-24 19:11:47 +00003270 while found and self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003271 found = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003272 index = self.queue.find(b"</event>")
3273 while index != -1 and self.queue.startswith(b"<event>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003274 try:
3275 event = pickle.loads(self.queue[7:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003276 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
3277 if isinstance(e, pickle.UnpicklingError) and "truncated" in str(e):
3278 # The pickled data could contain "</event>" so search for the next occurance
3279 # unpickling again, this should be the only way an unpickle error could occur
3280 index = self.queue.find(b"</event>", index + 1)
3281 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003282 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
3283 bb.event.fire_from_worker(event, self.d)
Brad Bishop08902b02019-08-20 09:16:51 -04003284 if isinstance(event, taskUniHashUpdate):
3285 self.rqexec.updated_taskhash_queue.append((event.taskid, event.unihash))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003286 found = True
3287 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003288 index = self.queue.find(b"</event>")
3289 index = self.queue.find(b"</exitcode>")
3290 while index != -1 and self.queue.startswith(b"<exitcode>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003291 try:
3292 task, status = pickle.loads(self.queue[10:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003293 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003294 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003295 (_, _, _, taskfn) = split_tid_mcfn(task)
3296 fakerootlog = None
3297 if self.fakerootlogs and taskfn and taskfn in self.fakerootlogs:
3298 fakerootlog = self.fakerootlogs[taskfn]
3299 self.rqexec.runqueue_process_waitpid(task, status, fakerootlog=fakerootlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003300 found = True
3301 self.queue = self.queue[index+11:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003302 index = self.queue.find(b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003303 return (end > start)
3304
3305 def close(self):
3306 while self.read():
3307 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00003308 if self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003309 print("Warning, worker left partial message: %s" % self.queue)
3310 self.input.close()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003311
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003312def get_setscene_enforce_ignore_tasks(d, targets):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003313 if d.getVar('BB_SETSCENE_ENFORCE') != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003314 return None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003315 ignore_tasks = (d.getVar("BB_SETSCENE_ENFORCE_IGNORE_TASKS") or "").split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003316 outlist = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003317 for item in ignore_tasks[:]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003318 if item.startswith('%:'):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003319 for (mc, target, task, fn) in targets:
3320 outlist.append(target + ':' + item.split(':')[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003321 else:
3322 outlist.append(item)
3323 return outlist
3324
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003325def check_setscene_enforce_ignore_tasks(pn, taskname, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003326 import fnmatch
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003327 if ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003328 item = '%s:%s' % (pn, taskname)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003329 for ignore_tasks in ignore_tasks:
3330 if fnmatch.fnmatch(item, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003331 return True
3332 return False
3333 return True