blob: 1e47fe70ef1705424e5bbbc09566a55bac46e9ac [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 Williamsc124f4f2015-09-15 14:41:29 -050027
28bblogger = logging.getLogger("BitBake")
29logger = logging.getLogger("BitBake.RunQueue")
Andrew Geissler82c905d2020-04-13 13:39:40 -050030hashequiv_logger = logging.getLogger("BitBake.RunQueue.HashEquiv")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031
Brad Bishop19323692019-04-05 15:28:33 -040032__find_sha256__ = re.compile( r'(?i)(?<![a-z0-9])[a-f0-9]{64}(?![a-z0-9])' )
Patrick Williamsc124f4f2015-09-15 14:41:29 -050033
Patrick Williamsc0f7c042017-02-23 20:41:17 -060034def fn_from_tid(tid):
35 return tid.rsplit(":", 1)[0]
36
37def taskname_from_tid(tid):
38 return tid.rsplit(":", 1)[1]
39
Andrew Geissler99467da2019-02-25 18:54:23 -060040def mc_from_tid(tid):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060041 if tid.startswith('mc:') and tid.count(':') >= 2:
Andrew Geissler99467da2019-02-25 18:54:23 -060042 return tid.split(':')[1]
43 return ""
44
Patrick Williamsc0f7c042017-02-23 20:41:17 -060045def split_tid(tid):
46 (mc, fn, taskname, _) = split_tid_mcfn(tid)
47 return (mc, fn, taskname)
48
Andrew Geissler5a43b432020-06-13 10:46:56 -050049def split_mc(n):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060050 if n.startswith("mc:") and n.count(':') >= 2:
Andrew Geissler5a43b432020-06-13 10:46:56 -050051 _, mc, n = n.split(":", 2)
52 return (mc, n)
53 return ('', n)
54
Patrick Williamsc0f7c042017-02-23 20:41:17 -060055def split_tid_mcfn(tid):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060056 if tid.startswith('mc:') and tid.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060057 elems = tid.split(':')
58 mc = elems[1]
59 fn = ":".join(elems[2:-1])
60 taskname = elems[-1]
Brad Bishop15ae2502019-06-18 21:44:24 -040061 mcfn = "mc:" + mc + ":" + fn
Patrick Williamsc0f7c042017-02-23 20:41:17 -060062 else:
63 tid = tid.rsplit(":", 1)
64 mc = ""
65 fn = tid[0]
66 taskname = tid[1]
67 mcfn = fn
68
69 return (mc, fn, taskname, mcfn)
70
71def build_tid(mc, fn, taskname):
72 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -040073 return "mc:" + mc + ":" + fn + ":" + taskname
Patrick Williamsc0f7c042017-02-23 20:41:17 -060074 return fn + ":" + taskname
75
Brad Bishop96ff1982019-08-19 13:50:42 -040076# Index used to pair up potentially matching multiconfig tasks
77# We match on PN, taskname and hash being equal
78def pending_hash_index(tid, rqdata):
79 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
80 pn = rqdata.dataCaches[mc].pkg_fn[taskfn]
Brad Bishop00e122a2019-10-05 11:10:57 -040081 h = rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -040082 return pn + ":" + "taskname" + h
83
Patrick Williamsc124f4f2015-09-15 14:41:29 -050084class RunQueueStats:
85 """
86 Holds statistics on the tasks handled by the associated runQueue
87 """
Andrew Geissler5199d832021-09-24 16:47:35 -050088 def __init__(self, total, setscene_total):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050089 self.completed = 0
90 self.skipped = 0
91 self.failed = 0
92 self.active = 0
Andrew Geissler5199d832021-09-24 16:47:35 -050093 self.setscene_active = 0
94 self.setscene_covered = 0
95 self.setscene_notcovered = 0
96 self.setscene_total = setscene_total
Patrick Williamsc124f4f2015-09-15 14:41:29 -050097 self.total = total
98
99 def copy(self):
Andrew Geissler5199d832021-09-24 16:47:35 -0500100 obj = self.__class__(self.total, self.setscene_total)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500101 obj.__dict__.update(self.__dict__)
102 return obj
103
104 def taskFailed(self):
105 self.active = self.active - 1
106 self.failed = self.failed + 1
107
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800108 def taskCompleted(self):
109 self.active = self.active - 1
110 self.completed = self.completed + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800112 def taskSkipped(self):
113 self.active = self.active + 1
114 self.skipped = self.skipped + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500115
116 def taskActive(self):
117 self.active = self.active + 1
118
Andrew Geissler5199d832021-09-24 16:47:35 -0500119 def updateCovered(self, covered, notcovered):
120 self.setscene_covered = covered
121 self.setscene_notcovered = notcovered
122
123 def updateActiveSetscene(self, active):
124 self.setscene_active = active
125
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500126# These values indicate the next step due to be run in the
127# runQueue state machine
128runQueuePrepare = 2
129runQueueSceneInit = 3
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500130runQueueRunning = 6
131runQueueFailed = 7
132runQueueCleanUp = 8
133runQueueComplete = 9
134
135class RunQueueScheduler(object):
136 """
137 Control the order tasks are scheduled in.
138 """
139 name = "basic"
140
141 def __init__(self, runqueue, rqdata):
142 """
143 The default scheduler just returns the first buildable task (the
144 priority map is sorted by task number)
145 """
146 self.rq = runqueue
147 self.rqdata = rqdata
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600148 self.numTasks = len(self.rqdata.runtaskentries)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500149
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600150 self.prio_map = [self.rqdata.runtaskentries.keys()]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500151
Brad Bishop08902b02019-08-20 09:16:51 -0400152 self.buildable = set()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800153 self.skip_maxthread = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500154 self.stamps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600155 for tid in self.rqdata.runtaskentries:
156 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
157 self.stamps[tid] = bb.build.stampfile(taskname, self.rqdata.dataCaches[mc], taskfn, noextra=True)
158 if tid in self.rq.runq_buildable:
159 self.buildable.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500160
161 self.rev_prio_map = None
162
163 def next_buildable_task(self):
164 """
165 Return the id of the first task we find that is buildable
166 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500167 # Once tasks are running we don't need to worry about them again
168 self.buildable.difference_update(self.rq.runq_running)
Brad Bishop08902b02019-08-20 09:16:51 -0400169 buildable = set(self.buildable)
Brad Bishop08902b02019-08-20 09:16:51 -0400170 buildable.difference_update(self.rq.holdoff_tasks)
171 buildable.intersection_update(self.rq.tasks_covered | self.rq.tasks_notcovered)
Brad Bishop96ff1982019-08-19 13:50:42 -0400172 if not buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500173 return None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800174
175 # Filter out tasks that have a max number of threads that have been exceeded
176 skip_buildable = {}
177 for running in self.rq.runq_running.difference(self.rq.runq_complete):
178 rtaskname = taskname_from_tid(running)
179 if rtaskname not in self.skip_maxthread:
180 self.skip_maxthread[rtaskname] = self.rq.cfgData.getVarFlag(rtaskname, "number_threads")
181 if not self.skip_maxthread[rtaskname]:
182 continue
183 if rtaskname in skip_buildable:
184 skip_buildable[rtaskname] += 1
185 else:
186 skip_buildable[rtaskname] = 1
187
Brad Bishop96ff1982019-08-19 13:50:42 -0400188 if len(buildable) == 1:
Brad Bishop08902b02019-08-20 09:16:51 -0400189 tid = buildable.pop()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800190 taskname = taskname_from_tid(tid)
191 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
192 return None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600193 stamp = self.stamps[tid]
194 if stamp not in self.rq.build_stamps.values():
195 return tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500196
197 if not self.rev_prio_map:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600198 self.rev_prio_map = {}
199 for tid in self.rqdata.runtaskentries:
200 self.rev_prio_map[tid] = self.prio_map.index(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500201
202 best = None
203 bestprio = None
Brad Bishop96ff1982019-08-19 13:50:42 -0400204 for tid in buildable:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800205 taskname = taskname_from_tid(tid)
206 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
207 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600208 prio = self.rev_prio_map[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500209 if bestprio is None or bestprio > prio:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600210 stamp = self.stamps[tid]
211 if stamp in self.rq.build_stamps.values():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500212 continue
213 bestprio = prio
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600214 best = tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500215
216 return best
217
218 def next(self):
219 """
220 Return the id of the task we should build next
221 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800222 if self.rq.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500223 return self.next_buildable_task()
224
Brad Bishop316dfdd2018-06-25 12:45:53 -0400225 def newbuildable(self, task):
Brad Bishop08902b02019-08-20 09:16:51 -0400226 self.buildable.add(task)
227
228 def removebuildable(self, task):
229 self.buildable.remove(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500230
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500231 def describe_task(self, taskid):
232 result = 'ID %s' % taskid
233 if self.rev_prio_map:
234 result = result + (' pri %d' % self.rev_prio_map[taskid])
235 return result
236
237 def dump_prio(self, comment):
238 bb.debug(3, '%s (most important first):\n%s' %
239 (comment,
240 '\n'.join(['%d. %s' % (index + 1, self.describe_task(taskid)) for
241 index, taskid in enumerate(self.prio_map)])))
242
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500243class RunQueueSchedulerSpeed(RunQueueScheduler):
244 """
245 A scheduler optimised for speed. The priority map is sorted by task weight,
246 heavier weighted tasks (tasks needed by the most other tasks) are run first.
247 """
248 name = "speed"
249
250 def __init__(self, runqueue, rqdata):
251 """
252 The priority map is sorted by task weight.
253 """
254 RunQueueScheduler.__init__(self, runqueue, rqdata)
255
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600256 weights = {}
257 for tid in self.rqdata.runtaskentries:
258 weight = self.rqdata.runtaskentries[tid].weight
259 if not weight in weights:
260 weights[weight] = []
261 weights[weight].append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600263 self.prio_map = []
264 for weight in sorted(weights):
265 for w in weights[weight]:
266 self.prio_map.append(w)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500267
268 self.prio_map.reverse()
269
270class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed):
271 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500272 A scheduler optimised to complete .bb files as quickly as possible. The
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500273 priority map is sorted by task weight, but then reordered so once a given
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500274 .bb file starts to build, it's completed as quickly as possible by
275 running all tasks related to the same .bb file one after the after.
276 This works well where disk space is at a premium and classes like OE's
277 rm_work are in force.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500278 """
279 name = "completion"
280
281 def __init__(self, runqueue, rqdata):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500282 super(RunQueueSchedulerCompletion, self).__init__(runqueue, rqdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500284 # Extract list of tasks for each recipe, with tasks sorted
285 # ascending from "must run first" (typically do_fetch) to
286 # "runs last" (do_build). The speed scheduler prioritizes
287 # tasks that must run first before the ones that run later;
288 # this is what we depend on here.
289 task_lists = {}
290 for taskid in self.prio_map:
291 fn, taskname = taskid.rsplit(':', 1)
292 task_lists.setdefault(fn, []).append(taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500293
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500294 # Now unify the different task lists. The strategy is that
295 # common tasks get skipped and new ones get inserted after the
296 # preceeding common one(s) as they are found. Because task
297 # lists should differ only by their number of tasks, but not
298 # the ordering of the common tasks, this should result in a
299 # deterministic result that is a superset of the individual
300 # task ordering.
301 all_tasks = []
302 for recipe, new_tasks in task_lists.items():
303 index = 0
304 old_task = all_tasks[index] if index < len(all_tasks) else None
305 for new_task in new_tasks:
306 if old_task == new_task:
307 # Common task, skip it. This is the fast-path which
308 # avoids a full search.
309 index += 1
310 old_task = all_tasks[index] if index < len(all_tasks) else None
311 else:
312 try:
313 index = all_tasks.index(new_task)
314 # Already present, just not at the current
315 # place. We re-synchronized by changing the
316 # index so that it matches again. Now
317 # move on to the next existing task.
318 index += 1
319 old_task = all_tasks[index] if index < len(all_tasks) else None
320 except ValueError:
321 # Not present. Insert before old_task, which
322 # remains the same (but gets shifted back).
323 all_tasks.insert(index, new_task)
324 index += 1
325 bb.debug(3, 'merged task list: %s' % all_tasks)
326
327 # Now reverse the order so that tasks that finish the work on one
328 # recipe are considered more imporant (= come first). The ordering
329 # is now so that do_build is most important.
330 all_tasks.reverse()
331
332 # Group tasks of the same kind before tasks of less important
333 # kinds at the head of the queue (because earlier = lower
334 # priority number = runs earlier), while preserving the
335 # ordering by recipe. If recipe foo is more important than
336 # bar, then the goal is to work on foo's do_populate_sysroot
337 # before bar's do_populate_sysroot and on the more important
338 # tasks of foo before any of the less important tasks in any
339 # other recipe (if those other recipes are more important than
340 # foo).
341 #
342 # All of this only applies when tasks are runable. Explicit
343 # dependencies still override this ordering by priority.
344 #
345 # Here's an example why this priority re-ordering helps with
346 # minimizing disk usage. Consider a recipe foo with a higher
347 # priority than bar where foo DEPENDS on bar. Then the
348 # implicit rule (from base.bbclass) is that foo's do_configure
349 # depends on bar's do_populate_sysroot. This ensures that
350 # bar's do_populate_sysroot gets done first. Normally the
351 # tasks from foo would continue to run once that is done, and
352 # bar only gets completed and cleaned up later. By ordering
353 # bar's task that depend on bar's do_populate_sysroot before foo's
354 # do_configure, that problem gets avoided.
355 task_index = 0
356 self.dump_prio('original priorities')
357 for task in all_tasks:
358 for index in range(task_index, self.numTasks):
359 taskid = self.prio_map[index]
360 taskname = taskid.rsplit(':', 1)[1]
361 if taskname == task:
362 del self.prio_map[index]
363 self.prio_map.insert(task_index, taskid)
364 task_index += 1
365 self.dump_prio('completion priorities')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500366
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600367class RunTaskEntry(object):
368 def __init__(self):
369 self.depends = set()
370 self.revdeps = set()
371 self.hash = None
Brad Bishop19323692019-04-05 15:28:33 -0400372 self.unihash = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600373 self.task = None
374 self.weight = 1
375
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500376class RunQueueData:
377 """
378 BitBake Run Queue implementation
379 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600380 def __init__(self, rq, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500381 self.cooker = cooker
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600382 self.dataCaches = dataCaches
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500383 self.taskData = taskData
384 self.targets = targets
385 self.rq = rq
386 self.warn_multi_bb = False
387
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000388 self.multi_provider_allowed = (cfgData.getVar("BB_MULTI_PROVIDER_ALLOWED") or "").split()
389 self.setscene_ignore_tasks = get_setscene_enforce_ignore_tasks(cfgData, targets)
390 self.setscene_ignore_tasks_checked = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500391 self.setscene_enforce = (cfgData.getVar('BB_SETSCENE_ENFORCE') == "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600392 self.init_progress_reporter = bb.progress.DummyMultiStageProcessProgressReporter()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500393
394 self.reset()
395
396 def reset(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600397 self.runtaskentries = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500398
399 def runq_depends_names(self, ids):
400 import re
401 ret = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600402 for id in ids:
403 nam = os.path.basename(id)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500404 nam = re.sub("_[^,]*,", ",", nam)
405 ret.extend([nam])
406 return ret
407
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600408 def get_task_hash(self, tid):
409 return self.runtaskentries[tid].hash
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500410
Brad Bishop19323692019-04-05 15:28:33 -0400411 def get_task_unihash(self, tid):
412 return self.runtaskentries[tid].unihash
413
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600414 def get_user_idstring(self, tid, task_name_suffix = ""):
415 return tid + task_name_suffix
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500416
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500417 def get_short_user_idstring(self, task, task_name_suffix = ""):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500418 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
419 pn = self.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600420 taskname = taskname_from_tid(task) + task_name_suffix
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500421 return "%s:%s" % (pn, taskname)
422
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500423 def circular_depchains_handler(self, tasks):
424 """
425 Some tasks aren't buildable, likely due to circular dependency issues.
426 Identify the circular dependencies and print them in a user readable format.
427 """
428 from copy import deepcopy
429
430 valid_chains = []
431 explored_deps = {}
432 msgs = []
433
Andrew Geissler99467da2019-02-25 18:54:23 -0600434 class TooManyLoops(Exception):
435 pass
436
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500437 def chain_reorder(chain):
438 """
439 Reorder a dependency chain so the lowest task id is first
440 """
441 lowest = 0
442 new_chain = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600443 for entry in range(len(chain)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444 if chain[entry] < chain[lowest]:
445 lowest = entry
446 new_chain.extend(chain[lowest:])
447 new_chain.extend(chain[:lowest])
448 return new_chain
449
450 def chain_compare_equal(chain1, chain2):
451 """
452 Compare two dependency chains and see if they're the same
453 """
454 if len(chain1) != len(chain2):
455 return False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600456 for index in range(len(chain1)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500457 if chain1[index] != chain2[index]:
458 return False
459 return True
460
461 def chain_array_contains(chain, chain_array):
462 """
463 Return True if chain_array contains chain
464 """
465 for ch in chain_array:
466 if chain_compare_equal(ch, chain):
467 return True
468 return False
469
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600470 def find_chains(tid, prev_chain):
471 prev_chain.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500472 total_deps = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600473 total_deps.extend(self.runtaskentries[tid].revdeps)
474 for revdep in self.runtaskentries[tid].revdeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500475 if revdep in prev_chain:
476 idx = prev_chain.index(revdep)
477 # To prevent duplicates, reorder the chain to start with the lowest taskid
478 # and search through an array of those we've already printed
479 chain = prev_chain[idx:]
480 new_chain = chain_reorder(chain)
481 if not chain_array_contains(new_chain, valid_chains):
482 valid_chains.append(new_chain)
483 msgs.append("Dependency loop #%d found:\n" % len(valid_chains))
484 for dep in new_chain:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600485 msgs.append(" Task %s (dependent Tasks %s)\n" % (dep, self.runq_depends_names(self.runtaskentries[dep].depends)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500486 msgs.append("\n")
487 if len(valid_chains) > 10:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000488 msgs.append("Halted dependency loops search after 10 matches.\n")
Andrew Geissler99467da2019-02-25 18:54:23 -0600489 raise TooManyLoops
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500490 continue
491 scan = False
492 if revdep not in explored_deps:
493 scan = True
494 elif revdep in explored_deps[revdep]:
495 scan = True
496 else:
497 for dep in prev_chain:
498 if dep in explored_deps[revdep]:
499 scan = True
500 if scan:
501 find_chains(revdep, copy.deepcopy(prev_chain))
502 for dep in explored_deps[revdep]:
503 if dep not in total_deps:
504 total_deps.append(dep)
505
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600506 explored_deps[tid] = total_deps
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500507
Andrew Geissler99467da2019-02-25 18:54:23 -0600508 try:
509 for task in tasks:
510 find_chains(task, [])
511 except TooManyLoops:
512 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500513
514 return msgs
515
516 def calculate_task_weights(self, endpoints):
517 """
518 Calculate a number representing the "weight" of each task. Heavier weighted tasks
519 have more dependencies and hence should be executed sooner for maximum speed.
520
521 This function also sanity checks the task list finding tasks that are not
522 possible to execute due to circular dependencies.
523 """
524
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600525 numTasks = len(self.runtaskentries)
526 weight = {}
527 deps_left = {}
528 task_done = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500529
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600530 for tid in self.runtaskentries:
531 task_done[tid] = False
532 weight[tid] = 1
533 deps_left[tid] = len(self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600535 for tid in endpoints:
536 weight[tid] = 10
537 task_done[tid] = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500538
539 while True:
540 next_points = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600541 for tid in endpoints:
542 for revdep in self.runtaskentries[tid].depends:
543 weight[revdep] = weight[revdep] + weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500544 deps_left[revdep] = deps_left[revdep] - 1
545 if deps_left[revdep] == 0:
546 next_points.append(revdep)
547 task_done[revdep] = True
548 endpoints = next_points
Andrew Geissler595f6302022-01-24 19:11:47 +0000549 if not next_points:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500550 break
551
552 # Circular dependency sanity check
553 problem_tasks = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600554 for tid in self.runtaskentries:
555 if task_done[tid] is False or deps_left[tid] != 0:
556 problem_tasks.append(tid)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600557 logger.debug2("Task %s is not buildable", tid)
558 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 -0600559 self.runtaskentries[tid].weight = weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500560
561 if problem_tasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600562 message = "%s unbuildable tasks were found.\n" % len(problem_tasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500563 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"
564 message = message + "Identifying dependency loops (this may take a short while)...\n"
565 logger.error(message)
566
567 msgs = self.circular_depchains_handler(problem_tasks)
568
569 message = "\n"
570 for msg in msgs:
571 message = message + msg
572 bb.msg.fatal("RunQueue", message)
573
574 return weight
575
576 def prepare(self):
577 """
578 Turn a set of taskData into a RunQueue and compute data needed
579 to optimise the execution order.
580 """
581
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600582 runq_build = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500583 recursivetasks = {}
584 recursiveitasks = {}
585 recursivetasksselfref = set()
586
587 taskData = self.taskData
588
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600589 found = False
590 for mc in self.taskData:
Andrew Geissler595f6302022-01-24 19:11:47 +0000591 if taskData[mc].taskentries:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600592 found = True
593 break
594 if not found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500595 # Nothing to do
596 return 0
597
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600598 self.init_progress_reporter.start()
599 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500600
601 # Step A - Work out a list of tasks to run
602 #
603 # Taskdata gives us a list of possible providers for every build and run
604 # target ordered by priority. It also gives information on each of those
605 # providers.
606 #
607 # To create the actual list of tasks to execute we fix the list of
608 # providers and then resolve the dependencies into task IDs. This
609 # process is repeated for each type of dependency (tdepends, deptask,
610 # rdeptast, recrdeptask, idepends).
611
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600612 def add_build_dependencies(depids, tasknames, depends, mc):
613 for depname in depids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500614 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600615 if depname not in taskData[mc].build_targets or not taskData[mc].build_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600617 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500618 if depdata is None:
619 continue
620 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600621 t = depdata + ":" + taskname
622 if t in taskData[mc].taskentries:
623 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500624
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600625 def add_runtime_dependencies(depids, tasknames, depends, mc):
626 for depname in depids:
627 if depname not in taskData[mc].run_targets or not taskData[mc].run_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500628 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600629 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500630 if depdata is None:
631 continue
632 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600633 t = depdata + ":" + taskname
634 if t in taskData[mc].taskentries:
635 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500636
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800637 def add_mc_dependencies(mc, tid):
638 mcdeps = taskData[mc].get_mcdepends()
639 for dep in mcdeps:
640 mcdependency = dep.split(':')
641 pn = mcdependency[3]
642 frommc = mcdependency[1]
643 mcdep = mcdependency[2]
644 deptask = mcdependency[4]
645 if mc == frommc:
646 fn = taskData[mcdep].build_targets[pn][0]
647 newdep = '%s:%s' % (fn,deptask)
648 taskData[mc].taskentries[tid].tdepends.append(newdep)
649
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600650 for mc in taskData:
651 for tid in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500652
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600653 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
654 #runtid = build_tid(mc, fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500655
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600656 #logger.debug2("Processing %s,%s:%s", mc, fn, taskname)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600657
658 depends = set()
659 task_deps = self.dataCaches[mc].task_deps[taskfn]
660
661 self.runtaskentries[tid] = RunTaskEntry()
662
663 if fn in taskData[mc].failed_fns:
664 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500665
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800666 # We add multiconfig dependencies before processing internal task deps (tdepends)
667 if 'mcdepends' in task_deps and taskname in task_deps['mcdepends']:
668 add_mc_dependencies(mc, tid)
669
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500670 # Resolve task internal dependencies
671 #
672 # e.g. addtask before X after Y
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600673 for t in taskData[mc].taskentries[tid].tdepends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800674 (depmc, depfn, deptaskname, _) = split_tid_mcfn(t)
675 depends.add(build_tid(depmc, depfn, deptaskname))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500676
677 # Resolve 'deptask' dependencies
678 #
679 # e.g. do_sometask[deptask] = "do_someothertask"
680 # (makes sure sometask runs after someothertask of all DEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600681 if 'deptask' in task_deps and taskname in task_deps['deptask']:
682 tasknames = task_deps['deptask'][taskname].split()
683 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500684
685 # Resolve 'rdeptask' dependencies
686 #
687 # e.g. do_sometask[rdeptask] = "do_someothertask"
688 # (makes sure sometask runs after someothertask of all RDEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600689 if 'rdeptask' in task_deps and taskname in task_deps['rdeptask']:
690 tasknames = task_deps['rdeptask'][taskname].split()
691 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500692
693 # Resolve inter-task dependencies
694 #
695 # e.g. do_sometask[depends] = "targetname:do_someothertask"
696 # (makes sure sometask runs after targetname's someothertask)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600697 idepends = taskData[mc].taskentries[tid].idepends
698 for (depname, idependtask) in idepends:
699 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 -0500700 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600701 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500702 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600703 t = depdata + ":" + idependtask
704 depends.add(t)
705 if t not in taskData[mc].taskentries:
706 bb.msg.fatal("RunQueue", "Task %s in %s depends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
707 irdepends = taskData[mc].taskentries[tid].irdepends
708 for (depname, idependtask) in irdepends:
709 if depname in taskData[mc].run_targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710 # Won't be in run_targets if ASSUME_PROVIDED
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500711 if not taskData[mc].run_targets[depname]:
712 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600713 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500714 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600715 t = depdata + ":" + idependtask
716 depends.add(t)
717 if t not in taskData[mc].taskentries:
718 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 -0500719
720 # Resolve recursive 'recrdeptask' dependencies (Part A)
721 #
722 # e.g. do_sometask[recrdeptask] = "do_someothertask"
723 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
724 # We cover the recursive part of the dependencies below
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600725 if 'recrdeptask' in task_deps and taskname in task_deps['recrdeptask']:
726 tasknames = task_deps['recrdeptask'][taskname].split()
727 recursivetasks[tid] = tasknames
728 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
729 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
730 if taskname in tasknames:
731 recursivetasksselfref.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500732
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600733 if 'recideptask' in task_deps and taskname in task_deps['recideptask']:
734 recursiveitasks[tid] = []
735 for t in task_deps['recideptask'][taskname].split():
736 newdep = build_tid(mc, fn, t)
737 recursiveitasks[tid].append(newdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500738
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600739 self.runtaskentries[tid].depends = depends
Brad Bishop316dfdd2018-06-25 12:45:53 -0400740 # Remove all self references
741 self.runtaskentries[tid].depends.discard(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500742
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600743 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500744
Brad Bishop316dfdd2018-06-25 12:45:53 -0400745 self.init_progress_reporter.next_stage()
746
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500747 # Resolve recursive 'recrdeptask' dependencies (Part B)
748 #
749 # e.g. do_sometask[recrdeptask] = "do_someothertask"
750 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600751 # 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 -0600752
Brad Bishop316dfdd2018-06-25 12:45:53 -0400753 # Generating/interating recursive lists of dependencies is painful and potentially slow
754 # Precompute recursive task dependencies here by:
755 # a) create a temp list of reverse dependencies (revdeps)
756 # b) walk up the ends of the chains (when a given task no longer has dependencies i.e. len(deps) == 0)
757 # c) combine the total list of dependencies in cumulativedeps
758 # d) optimise by pre-truncating 'task' off the items in cumulativedeps (keeps items in sets lower)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500760
Brad Bishop316dfdd2018-06-25 12:45:53 -0400761 revdeps = {}
762 deps = {}
763 cumulativedeps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600764 for tid in self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400765 deps[tid] = set(self.runtaskentries[tid].depends)
766 revdeps[tid] = set()
767 cumulativedeps[tid] = set()
768 # Generate a temp list of reverse dependencies
769 for tid in self.runtaskentries:
770 for dep in self.runtaskentries[tid].depends:
771 revdeps[dep].add(tid)
772 # Find the dependency chain endpoints
773 endpoints = set()
774 for tid in self.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +0000775 if not deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400776 endpoints.add(tid)
777 # Iterate the chains collating dependencies
778 while endpoints:
779 next = set()
780 for tid in endpoints:
781 for dep in revdeps[tid]:
782 cumulativedeps[dep].add(fn_from_tid(tid))
783 cumulativedeps[dep].update(cumulativedeps[tid])
784 if tid in deps[dep]:
785 deps[dep].remove(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +0000786 if not deps[dep]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400787 next.add(dep)
788 endpoints = next
789 #for tid in deps:
Andrew Geissler595f6302022-01-24 19:11:47 +0000790 # if deps[tid]:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400791 # bb.warn("Sanity test failure, dependencies left for %s (%s)" % (tid, deps[tid]))
792
793 # Loop here since recrdeptasks can depend upon other recrdeptasks and we have to
794 # resolve these recursively until we aren't adding any further extra dependencies
795 extradeps = True
796 while extradeps:
797 extradeps = 0
798 for tid in recursivetasks:
799 tasknames = recursivetasks[tid]
800
801 totaldeps = set(self.runtaskentries[tid].depends)
802 if tid in recursiveitasks:
803 totaldeps.update(recursiveitasks[tid])
804 for dep in recursiveitasks[tid]:
805 if dep not in self.runtaskentries:
806 continue
807 totaldeps.update(self.runtaskentries[dep].depends)
808
809 deps = set()
810 for dep in totaldeps:
811 if dep in cumulativedeps:
812 deps.update(cumulativedeps[dep])
813
814 for t in deps:
815 for taskname in tasknames:
816 newtid = t + ":" + taskname
817 if newtid == tid:
818 continue
819 if newtid in self.runtaskentries and newtid not in self.runtaskentries[tid].depends:
820 extradeps += 1
821 self.runtaskentries[tid].depends.add(newtid)
822
823 # Handle recursive tasks which depend upon other recursive tasks
824 deps = set()
825 for dep in self.runtaskentries[tid].depends.intersection(recursivetasks):
826 deps.update(self.runtaskentries[dep].depends.difference(self.runtaskentries[tid].depends))
827 for newtid in deps:
828 for taskname in tasknames:
829 if not newtid.endswith(":" + taskname):
830 continue
831 if newtid in self.runtaskentries:
832 extradeps += 1
833 self.runtaskentries[tid].depends.add(newtid)
834
835 bb.debug(1, "Added %s recursive dependencies in this loop" % extradeps)
836
837 # Remove recrdeptask circular references so that do_a[recrdeptask] = "do_a do_b" can work
838 for tid in recursivetasksselfref:
839 self.runtaskentries[tid].depends.difference_update(recursivetasksselfref)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600840
841 self.init_progress_reporter.next_stage()
842
843 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844
845 # Step B - Mark all active tasks
846 #
847 # Start with the tasks we were asked to run and mark all dependencies
848 # as active too. If the task is to be 'forced', clear its stamp. Once
849 # all active tasks are marked, prune the ones we don't need.
850
851 logger.verbose("Marking Active Tasks")
852
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600853 def mark_active(tid, depth):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500854 """
855 Mark an item as active along with its depends
856 (calls itself recursively)
857 """
858
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600859 if tid in runq_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500860 return
861
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600862 runq_build[tid] = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500863
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600864 depends = self.runtaskentries[tid].depends
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500865 for depend in depends:
866 mark_active(depend, depth+1)
867
Brad Bishop79641f22019-09-10 07:20:22 -0400868 def invalidate_task(tid, error_nostamp):
869 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
870 taskdep = self.dataCaches[mc].task_deps[taskfn]
871 if fn + ":" + taskname not in taskData[mc].taskentries:
872 logger.warning("Task %s does not exist, invalidating this task will have no effect" % taskname)
873 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
874 if error_nostamp:
875 bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
876 else:
877 bb.debug(1, "Task %s is marked nostamp, cannot invalidate this task" % taskname)
878 else:
879 logger.verbose("Invalidate task %s, %s", taskname, fn)
880 bb.parse.siggen.invalidate_task(taskname, self.dataCaches[mc], taskfn)
881
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600882 self.target_tids = []
883 for (mc, target, task, fn) in self.targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500884
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600885 if target not in taskData[mc].build_targets or not taskData[mc].build_targets[target]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500886 continue
887
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600888 if target in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500889 continue
890
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500891 parents = False
892 if task.endswith('-'):
893 parents = True
894 task = task[:-1]
895
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600896 if fn in taskData[mc].failed_fns:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500897 continue
898
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600899 # fn already has mc prefix
900 tid = fn + ":" + task
901 self.target_tids.append(tid)
902 if tid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500903 import difflib
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600904 tasks = []
905 for x in taskData[mc].taskentries:
906 if x.startswith(fn + ":"):
907 tasks.append(taskname_from_tid(x))
908 close_matches = difflib.get_close_matches(task, tasks, cutoff=0.7)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500909 if close_matches:
910 extra = ". Close matches:\n %s" % "\n ".join(close_matches)
911 else:
912 extra = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600913 bb.msg.fatal("RunQueue", "Task %s does not exist for target %s (%s)%s" % (task, target, tid, extra))
914
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500915 # For tasks called "XXXX-", ony run their dependencies
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500916 if parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600917 for i in self.runtaskentries[tid].depends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500918 mark_active(i, 1)
919 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600920 mark_active(tid, 1)
921
922 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500923
924 # Step C - Prune all inactive tasks
925 #
926 # Once all active tasks are marked, prune the ones we don't need.
927
Brad Bishop316dfdd2018-06-25 12:45:53 -0400928 # Handle --runall
929 if self.cooker.configuration.runall:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500930 # re-run the mark_active and then drop unused tasks from new list
Andrew Geissler595f6302022-01-24 19:11:47 +0000931 reduced_tasklist = set(self.runtaskentries.keys())
932 for tid in list(self.runtaskentries.keys()):
933 if tid not in runq_build:
934 reduced_tasklist.remove(tid)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500935 runq_build = {}
Brad Bishop316dfdd2018-06-25 12:45:53 -0400936
937 for task in self.cooker.configuration.runall:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500938 if not task.startswith("do_"):
939 task = "do_{0}".format(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400940 runall_tids = set()
Andrew Geissler595f6302022-01-24 19:11:47 +0000941 for tid in reduced_tasklist:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500942 wanttid = "{0}:{1}".format(fn_from_tid(tid), task)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400943 if wanttid in self.runtaskentries:
944 runall_tids.add(wanttid)
945
946 for tid in list(runall_tids):
Andrew Geissler595f6302022-01-24 19:11:47 +0000947 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -0400948 if self.cooker.configuration.force:
949 invalidate_task(tid, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500950
Andrew Geissler595f6302022-01-24 19:11:47 +0000951 delcount = set()
952 for tid in list(self.runtaskentries.keys()):
953 if tid not in runq_build:
954 delcount.add(tid)
955 del self.runtaskentries[tid]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500956
Andrew Geissler595f6302022-01-24 19:11:47 +0000957 if self.cooker.configuration.runall:
958 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400959 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)))
960
961 self.init_progress_reporter.next_stage()
962
963 # Handle runonly
964 if self.cooker.configuration.runonly:
965 # re-run the mark_active and then drop unused tasks from new list
966 runq_build = {}
967
968 for task in self.cooker.configuration.runonly:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500969 if not task.startswith("do_"):
970 task = "do_{0}".format(task)
Andrew Geissler595f6302022-01-24 19:11:47 +0000971 runonly_tids = [k for k in self.runtaskentries.keys() if taskname_from_tid(k) == task]
Brad Bishop316dfdd2018-06-25 12:45:53 -0400972
Andrew Geissler595f6302022-01-24 19:11:47 +0000973 for tid in runonly_tids:
974 mark_active(tid, 1)
Brad Bishop79641f22019-09-10 07:20:22 -0400975 if self.cooker.configuration.force:
976 invalidate_task(tid, False)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400977
978 for tid in list(self.runtaskentries.keys()):
979 if tid not in runq_build:
Andrew Geissler595f6302022-01-24 19:11:47 +0000980 delcount.add(tid)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400981 del self.runtaskentries[tid]
982
Andrew Geissler595f6302022-01-24 19:11:47 +0000983 if not self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400984 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 -0500985
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500986 #
987 # Step D - Sanity checks and computation
988 #
989
990 # Check to make sure we still have tasks to run
Andrew Geissler595f6302022-01-24 19:11:47 +0000991 if not self.runtaskentries:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000992 if not taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500993 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.")
994 else:
995 bb.msg.fatal("RunQueue", "No active tasks and not in --continue mode?! Please report this bug.")
996
Brad Bishop316dfdd2018-06-25 12:45:53 -0400997 logger.verbose("Pruned %s inactive tasks, %s left", len(delcount), len(self.runtaskentries))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500998
999 logger.verbose("Assign Weightings")
1000
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001001 self.init_progress_reporter.next_stage()
1002
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001003 # Generate a list of reverse dependencies to ease future calculations
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001004 for tid in self.runtaskentries:
1005 for dep in self.runtaskentries[tid].depends:
1006 self.runtaskentries[dep].revdeps.add(tid)
1007
1008 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001009
1010 # Identify tasks at the end of dependency chains
1011 # Error on circular dependency loops (length two)
1012 endpoints = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001013 for tid in self.runtaskentries:
1014 revdeps = self.runtaskentries[tid].revdeps
Andrew Geissler595f6302022-01-24 19:11:47 +00001015 if not revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001016 endpoints.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001017 for dep in revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001018 if dep in self.runtaskentries[tid].depends:
1019 bb.msg.fatal("RunQueue", "Task %s has circular dependency on %s" % (tid, dep))
1020
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001021
1022 logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
1023
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001024 self.init_progress_reporter.next_stage()
1025
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001026 # Calculate task weights
1027 # Check of higher length circular dependencies
1028 self.runq_weight = self.calculate_task_weights(endpoints)
1029
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001030 self.init_progress_reporter.next_stage()
1031
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001032 # Sanity Check - Check for multiple tasks building the same provider
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001033 for mc in self.dataCaches:
1034 prov_list = {}
1035 seen_fn = []
1036 for tid in self.runtaskentries:
1037 (tidmc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1038 if taskfn in seen_fn:
1039 continue
1040 if mc != tidmc:
1041 continue
1042 seen_fn.append(taskfn)
1043 for prov in self.dataCaches[mc].fn_provides[taskfn]:
1044 if prov not in prov_list:
1045 prov_list[prov] = [taskfn]
1046 elif taskfn not in prov_list[prov]:
1047 prov_list[prov].append(taskfn)
1048 for prov in prov_list:
1049 if len(prov_list[prov]) < 2:
1050 continue
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001051 if prov in self.multi_provider_allowed:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001052 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001053 seen_pn = []
1054 # If two versions of the same PN are being built its fatal, we don't support it.
1055 for fn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001056 pn = self.dataCaches[mc].pkg_fn[fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001057 if pn not in seen_pn:
1058 seen_pn.append(pn)
1059 else:
1060 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 +00001061 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 -05001062 #
1063 # Construct a list of things which uniquely depend on each provider
1064 # since this may help the user figure out which dependency is triggering this warning
1065 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001066 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 -05001067 deplist = {}
1068 commondeps = None
1069 for provfn in prov_list[prov]:
1070 deps = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001071 for tid in self.runtaskentries:
1072 fn = fn_from_tid(tid)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001073 if fn != provfn:
1074 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001075 for dep in self.runtaskentries[tid].revdeps:
1076 fn = fn_from_tid(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001077 if fn == provfn:
1078 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001079 deps.add(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001080 if not commondeps:
1081 commondeps = set(deps)
1082 else:
1083 commondeps &= deps
1084 deplist[provfn] = deps
1085 for provfn in deplist:
Andrew Geissler595f6302022-01-24 19:11:47 +00001086 msgs.append("\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001087 #
1088 # Construct a list of provides and runtime providers for each recipe
1089 # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC)
1090 #
Andrew Geissler595f6302022-01-24 19:11:47 +00001091 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 -05001092 provide_results = {}
1093 rprovide_results = {}
1094 commonprovs = None
1095 commonrprovs = None
1096 for provfn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001097 provides = set(self.dataCaches[mc].fn_provides[provfn])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001098 rprovides = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001099 for rprovide in self.dataCaches[mc].rproviders:
1100 if provfn in self.dataCaches[mc].rproviders[rprovide]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001101 rprovides.add(rprovide)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001102 for package in self.dataCaches[mc].packages:
1103 if provfn in self.dataCaches[mc].packages[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001104 rprovides.add(package)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001105 for package in self.dataCaches[mc].packages_dynamic:
1106 if provfn in self.dataCaches[mc].packages_dynamic[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001107 rprovides.add(package)
1108 if not commonprovs:
1109 commonprovs = set(provides)
1110 else:
1111 commonprovs &= provides
1112 provide_results[provfn] = provides
1113 if not commonrprovs:
1114 commonrprovs = set(rprovides)
1115 else:
1116 commonrprovs &= rprovides
1117 rprovide_results[provfn] = rprovides
Andrew Geissler595f6302022-01-24 19:11:47 +00001118 #msgs.append("\nCommon provides:\n %s" % ("\n ".join(commonprovs)))
1119 #msgs.append("\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001120 for provfn in prov_list[prov]:
Andrew Geissler595f6302022-01-24 19:11:47 +00001121 msgs.append("\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs)))
1122 msgs.append("\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001123
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001124 if self.warn_multi_bb:
Andrew Geissler595f6302022-01-24 19:11:47 +00001125 logger.verbnote("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001126 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00001127 logger.error("".join(msgs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001128
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001129 self.init_progress_reporter.next_stage()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001130 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001131
1132 # Iterate over the task list looking for tasks with a 'setscene' function
Andrew Geissler82c905d2020-04-13 13:39:40 -05001133 self.runq_setscene_tids = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001134 if not self.cooker.configuration.nosetscene:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001135 for tid in self.runtaskentries:
1136 (mc, fn, taskname, _) = split_tid_mcfn(tid)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001137 setscenetid = tid + "_setscene"
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001138 if setscenetid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001139 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -05001140 self.runq_setscene_tids.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001141
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001142 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001143
1144 # Invalidate task if force mode active
1145 if self.cooker.configuration.force:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001146 for tid in self.target_tids:
1147 invalidate_task(tid, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001148
1149 # Invalidate task if invalidate mode active
1150 if self.cooker.configuration.invalidate_stamp:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001151 for tid in self.target_tids:
1152 fn = fn_from_tid(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001153 for st in self.cooker.configuration.invalidate_stamp.split(','):
1154 if not st.startswith("do_"):
1155 st = "do_%s" % st
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001156 invalidate_task(fn + ":" + st, True)
1157
1158 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001159
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001160 # Create and print to the logs a virtual/xxxx -> PN (fn) table
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001161 for mc in taskData:
1162 virtmap = taskData[mc].get_providermap(prefix="virtual/")
1163 virtpnmap = {}
1164 for v in virtmap:
1165 virtpnmap[v] = self.dataCaches[mc].pkg_fn[virtmap[v]]
1166 bb.debug(2, "%s resolved to: %s (%s)" % (v, virtpnmap[v], virtmap[v]))
1167 if hasattr(bb.parse.siggen, "tasks_resolved"):
1168 bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
1169
1170 self.init_progress_reporter.next_stage()
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001171
Brad Bishop00e122a2019-10-05 11:10:57 -04001172 bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
1173
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001174 # Iterate over the task list and call into the siggen code
1175 dealtwith = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001176 todeal = set(self.runtaskentries)
Andrew Geissler595f6302022-01-24 19:11:47 +00001177 while todeal:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001178 for tid in todeal.copy():
Andrew Geissler595f6302022-01-24 19:11:47 +00001179 if not (self.runtaskentries[tid].depends - dealtwith):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001180 dealtwith.add(tid)
1181 todeal.remove(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001182 self.prepare_task_hash(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001183
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001184 bb.parse.siggen.writeout_file_checksum_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001185
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001186 #self.dump_data()
1187 return len(self.runtaskentries)
1188
Brad Bishop19323692019-04-05 15:28:33 -04001189 def prepare_task_hash(self, tid):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001190 dc = bb.parse.siggen.get_data_caches(self.dataCaches, mc_from_tid(tid))
1191 bb.parse.siggen.prep_taskhash(tid, self.runtaskentries[tid].depends, dc)
1192 self.runtaskentries[tid].hash = bb.parse.siggen.get_taskhash(tid, self.runtaskentries[tid].depends, dc)
Brad Bishop08902b02019-08-20 09:16:51 -04001193 self.runtaskentries[tid].unihash = bb.parse.siggen.get_unihash(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001194
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001195 def dump_data(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001196 """
1197 Dump some debug information on the internal data structures
1198 """
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001199 logger.debug3("run_tasks:")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001200 for tid in self.runtaskentries:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001201 logger.debug3(" %s: %s Deps %s RevDeps %s", tid,
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001202 self.runtaskentries[tid].weight,
1203 self.runtaskentries[tid].depends,
1204 self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001205
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001206class RunQueueWorker():
1207 def __init__(self, process, pipe):
1208 self.process = process
1209 self.pipe = pipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001210
1211class RunQueue:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001212 def __init__(self, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001213
1214 self.cooker = cooker
1215 self.cfgData = cfgData
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001216 self.rqdata = RunQueueData(self, cooker, cfgData, dataCaches, taskData, targets)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001217
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001218 self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION") or None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001219 self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID") or None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001220
1221 self.state = runQueuePrepare
1222
1223 # For disk space monitor
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001224 # Invoked at regular time intervals via the bitbake heartbeat event
1225 # while the build is running. We generate a unique name for the handler
1226 # here, just in case that there ever is more than one RunQueue instance,
Brad Bishop96ff1982019-08-19 13:50:42 -04001227 # start the handler when reaching runQueueSceneInit, and stop it when
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001228 # done with the build.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001229 self.dm = monitordisk.diskMonitor(cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001230 self.dm_event_handler_name = '_bb_diskmonitor_' + str(id(self))
1231 self.dm_event_handler_registered = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001232 self.rqexe = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001233 self.worker = {}
1234 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001235
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001236 def _start_worker(self, mc, fakeroot = False, rqexec = None):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001237 logger.debug("Starting bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001238 magic = "decafbad"
1239 if self.cooker.configuration.profile:
1240 magic = "decafbadbad"
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001241 fakerootlogs = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001242 if fakeroot:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001243 magic = magic + "beef"
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001244 mcdata = self.cooker.databuilder.mcdata[mc]
Brad Bishop19323692019-04-05 15:28:33 -04001245 fakerootcmd = shlex.split(mcdata.getVar("FAKEROOTCMD"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001246 fakerootenv = (mcdata.getVar("FAKEROOTBASEENV") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001247 env = os.environ.copy()
1248 for key, value in (var.split('=') for var in fakerootenv):
1249 env[key] = value
Brad Bishop19323692019-04-05 15:28:33 -04001250 worker = subprocess.Popen(fakerootcmd + ["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001251 fakerootlogs = self.rqdata.dataCaches[mc].fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001252 else:
1253 worker = subprocess.Popen(["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
1254 bb.utils.nonblockingfd(worker.stdout)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001255 workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, self, rqexec, fakerootlogs=fakerootlogs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001256
1257 workerdata = {
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001258 "taskdeps" : self.rqdata.dataCaches[mc].task_deps,
1259 "fakerootenv" : self.rqdata.dataCaches[mc].fakerootenv,
1260 "fakerootdirs" : self.rqdata.dataCaches[mc].fakerootdirs,
1261 "fakerootnoenv" : self.rqdata.dataCaches[mc].fakerootnoenv,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001262 "sigdata" : bb.parse.siggen.get_taskdata(),
Andrew Geissler82c905d2020-04-13 13:39:40 -05001263 "logdefaultlevel" : bb.msg.loggerDefaultLogLevel,
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001264 "build_verbose_shell" : self.cooker.configuration.build_verbose_shell,
1265 "build_verbose_stdout" : self.cooker.configuration.build_verbose_stdout,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001266 "logdefaultdomain" : bb.msg.loggerDefaultDomains,
1267 "prhost" : self.cooker.prhost,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001268 "buildname" : self.cfgData.getVar("BUILDNAME"),
1269 "date" : self.cfgData.getVar("DATE"),
1270 "time" : self.cfgData.getVar("TIME"),
Brad Bishopa34c0302019-09-23 22:34:48 -04001271 "hashservaddr" : self.cooker.hashservaddr,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001272 "umask" : self.cfgData.getVar("BB_DEFAULT_UMASK"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001273 }
1274
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001275 worker.stdin.write(b"<cookerconfig>" + pickle.dumps(self.cooker.configuration) + b"</cookerconfig>")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001276 worker.stdin.write(b"<extraconfigdata>" + pickle.dumps(self.cooker.extraconfigdata) + b"</extraconfigdata>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001277 worker.stdin.write(b"<workerdata>" + pickle.dumps(workerdata) + b"</workerdata>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001278 worker.stdin.flush()
1279
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001280 return RunQueueWorker(worker, workerpipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001281
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001282 def _teardown_worker(self, worker):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001283 if not worker:
1284 return
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001285 logger.debug("Teardown for bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001286 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001287 worker.process.stdin.write(b"<quit></quit>")
1288 worker.process.stdin.flush()
1289 worker.process.stdin.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001290 except IOError:
1291 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001292 while worker.process.returncode is None:
1293 worker.pipe.read()
1294 worker.process.poll()
1295 while worker.pipe.read():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001296 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001297 worker.pipe.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001298
1299 def start_worker(self):
1300 if self.worker:
1301 self.teardown_workers()
1302 self.teardown = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001303 for mc in self.rqdata.dataCaches:
1304 self.worker[mc] = self._start_worker(mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001305
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001306 def start_fakeworker(self, rqexec, mc):
1307 if not mc in self.fakeworker:
1308 self.fakeworker[mc] = self._start_worker(mc, True, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001309
1310 def teardown_workers(self):
1311 self.teardown = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001312 for mc in self.worker:
1313 self._teardown_worker(self.worker[mc])
1314 self.worker = {}
1315 for mc in self.fakeworker:
1316 self._teardown_worker(self.fakeworker[mc])
1317 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001318
1319 def read_workers(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001320 for mc in self.worker:
1321 self.worker[mc].pipe.read()
1322 for mc in self.fakeworker:
1323 self.fakeworker[mc].pipe.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001324
1325 def active_fds(self):
1326 fds = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001327 for mc in self.worker:
1328 fds.append(self.worker[mc].pipe.input)
1329 for mc in self.fakeworker:
1330 fds.append(self.fakeworker[mc].pipe.input)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001331 return fds
1332
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001333 def check_stamp_task(self, tid, taskname = None, recurse = False, cache = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334 def get_timestamp(f):
1335 try:
1336 if not os.access(f, os.F_OK):
1337 return None
1338 return os.stat(f)[stat.ST_MTIME]
1339 except:
1340 return None
1341
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001342 (mc, fn, tn, taskfn) = split_tid_mcfn(tid)
1343 if taskname is None:
1344 taskname = tn
1345
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001346 stampfile = bb.build.stampfile(taskname, self.rqdata.dataCaches[mc], taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001347
1348 # If the stamp is missing, it's not current
1349 if not os.access(stampfile, os.F_OK):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001350 logger.debug2("Stampfile %s not available", stampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001351 return False
1352 # If it's a 'nostamp' task, it's not current
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001353 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001354 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001355 logger.debug2("%s.%s is nostamp\n", fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001356 return False
1357
1358 if taskname != "do_setscene" and taskname.endswith("_setscene"):
1359 return True
1360
1361 if cache is None:
1362 cache = {}
1363
1364 iscurrent = True
1365 t1 = get_timestamp(stampfile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001366 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001367 if iscurrent:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001368 (mc2, fn2, taskname2, taskfn2) = split_tid_mcfn(dep)
1369 stampfile2 = bb.build.stampfile(taskname2, self.rqdata.dataCaches[mc2], taskfn2)
1370 stampfile3 = bb.build.stampfile(taskname2 + "_setscene", self.rqdata.dataCaches[mc2], taskfn2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001371 t2 = get_timestamp(stampfile2)
1372 t3 = get_timestamp(stampfile3)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001373 if t3 and not t2:
1374 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001375 if t3 and t3 > t2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001376 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00001377 if fn == fn2:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001378 if not t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001379 logger.debug2('Stampfile %s does not exist', stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001380 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001381 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001382 if t1 < t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001383 logger.debug2('Stampfile %s < %s', stampfile, stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001384 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001385 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001386 if recurse and iscurrent:
1387 if dep in cache:
1388 iscurrent = cache[dep]
1389 if not iscurrent:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001390 logger.debug2('Stampfile for dependency %s:%s invalid (cached)' % (fn2, taskname2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001391 else:
1392 iscurrent = self.check_stamp_task(dep, recurse=True, cache=cache)
1393 cache[dep] = iscurrent
1394 if recurse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001395 cache[tid] = iscurrent
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001396 return iscurrent
1397
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001398 def validate_hashes(self, tocheck, data, currentcount=0, siginfo=False, summary=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04001399 valid = set()
1400 if self.hashvalidate:
Brad Bishop08902b02019-08-20 09:16:51 -04001401 sq_data = {}
1402 sq_data['hash'] = {}
1403 sq_data['hashfn'] = {}
1404 sq_data['unihash'] = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04001405 for tid in tocheck:
1406 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04001407 sq_data['hash'][tid] = self.rqdata.runtaskentries[tid].hash
1408 sq_data['hashfn'][tid] = self.rqdata.dataCaches[mc].hashfn[taskfn]
1409 sq_data['unihash'][tid] = self.rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -04001410
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001411 valid = self.validate_hash(sq_data, data, siginfo, currentcount, summary)
Brad Bishop96ff1982019-08-19 13:50:42 -04001412
1413 return valid
1414
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001415 def validate_hash(self, sq_data, d, siginfo, currentcount, summary):
1416 locs = {"sq_data" : sq_data, "d" : d, "siginfo" : siginfo, "currentcount" : currentcount, "summary" : summary}
Brad Bishop19323692019-04-05 15:28:33 -04001417
Brad Bishop08902b02019-08-20 09:16:51 -04001418 # Metadata has **kwargs so args can be added, sq_data can also gain new fields
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001419 call = self.hashvalidate + "(sq_data, d, siginfo=siginfo, currentcount=currentcount, summary=summary)"
Brad Bishop19323692019-04-05 15:28:33 -04001420
Brad Bishop19323692019-04-05 15:28:33 -04001421 return bb.utils.better_eval(call, locs)
1422
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001423 def _execute_runqueue(self):
1424 """
1425 Run the tasks in a queue prepared by rqdata.prepare()
1426 Upon failure, optionally try to recover the build using any alternate providers
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001427 (if the halt on failure configuration option isn't set)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001428 """
1429
1430 retval = True
1431
1432 if self.state is runQueuePrepare:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001433 # NOTE: if you add, remove or significantly refactor the stages of this
1434 # process then you should recalculate the weightings here. This is quite
1435 # easy to do - just change the next line temporarily to pass debug=True as
1436 # the last parameter and you'll get a printout of the weightings as well
1437 # as a map to the lines where next_stage() was called. Of course this isn't
1438 # critical, but it helps to keep the progress reporting accurate.
1439 self.rqdata.init_progress_reporter = bb.progress.MultiStageProcessProgressReporter(self.cooker.data,
1440 "Initialising tasks",
1441 [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 -05001442 if self.rqdata.prepare() == 0:
1443 self.state = runQueueComplete
1444 else:
1445 self.state = runQueueSceneInit
Brad Bishop00e122a2019-10-05 11:10:57 -04001446 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001447
1448 if self.state is runQueueSceneInit:
Brad Bishop96ff1982019-08-19 13:50:42 -04001449 self.rqdata.init_progress_reporter.next_stage()
1450
1451 # we are ready to run, emit dependency info to any UI or class which
1452 # needs it
1453 depgraph = self.cooker.buildDependTree(self, self.rqdata.taskData)
1454 self.rqdata.init_progress_reporter.next_stage()
1455 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
1456
Brad Bishope2d5b612018-11-23 10:55:50 +13001457 if not self.dm_event_handler_registered:
1458 res = bb.event.register(self.dm_event_handler_name,
Brad Bishop96ff1982019-08-19 13:50:42 -04001459 lambda x: self.dm.check(self) if self.state in [runQueueRunning, runQueueCleanUp] else False,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001460 ('bb.event.HeartbeatEvent',), data=self.cfgData)
Brad Bishope2d5b612018-11-23 10:55:50 +13001461 self.dm_event_handler_registered = True
1462
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001463 dump = self.cooker.configuration.dump_signatures
1464 if dump:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001465 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001466 if 'printdiff' in dump:
1467 invalidtasks = self.print_diffscenetasks()
1468 self.dump_signatures(dump)
1469 if 'printdiff' in dump:
1470 self.write_diffscenetasks(invalidtasks)
1471 self.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001472
Brad Bishop96ff1982019-08-19 13:50:42 -04001473 if self.state is runQueueSceneInit:
1474 self.rqdata.init_progress_reporter.next_stage()
1475 self.start_worker()
1476 self.rqdata.init_progress_reporter.next_stage()
1477 self.rqexe = RunQueueExecute(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001478
Brad Bishop96ff1982019-08-19 13:50:42 -04001479 # If we don't have any setscene functions, skip execution
Andrew Geissler595f6302022-01-24 19:11:47 +00001480 if not self.rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04001481 logger.info('No setscene tasks')
1482 for tid in self.rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00001483 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04001484 self.rqexe.setbuildable(tid)
1485 self.rqexe.tasks_notcovered.add(tid)
1486 self.rqexe.sqdone = True
1487 logger.info('Executing Tasks')
1488 self.state = runQueueRunning
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001489
1490 if self.state is runQueueRunning:
1491 retval = self.rqexe.execute()
1492
1493 if self.state is runQueueCleanUp:
1494 retval = self.rqexe.finish()
1495
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001496 build_done = self.state is runQueueComplete or self.state is runQueueFailed
1497
1498 if build_done and self.dm_event_handler_registered:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001499 bb.event.remove(self.dm_event_handler_name, None, data=self.cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001500 self.dm_event_handler_registered = False
1501
1502 if build_done and self.rqexe:
Brad Bishop08902b02019-08-20 09:16:51 -04001503 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001504 self.teardown_workers()
Brad Bishop96ff1982019-08-19 13:50:42 -04001505 if self.rqexe:
1506 if self.rqexe.stats.failed:
1507 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)
1508 else:
1509 # Let's avoid the word "failed" if nothing actually did
1510 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 -05001511
1512 if self.state is runQueueFailed:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001513 raise bb.runqueue.TaskFailure(self.rqexe.failed_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001514
1515 if self.state is runQueueComplete:
1516 # All done
1517 return False
1518
1519 # Loop
1520 return retval
1521
1522 def execute_runqueue(self):
1523 # Catch unexpected exceptions and ensure we exit when an error occurs, not loop.
1524 try:
1525 return self._execute_runqueue()
1526 except bb.runqueue.TaskFailure:
1527 raise
1528 except SystemExit:
1529 raise
1530 except bb.BBHandledException:
1531 try:
1532 self.teardown_workers()
1533 except:
1534 pass
1535 self.state = runQueueComplete
1536 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001537 except Exception as err:
1538 logger.exception("An uncaught exception occurred in runqueue")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001539 try:
1540 self.teardown_workers()
1541 except:
1542 pass
1543 self.state = runQueueComplete
1544 raise
1545
1546 def finish_runqueue(self, now = False):
1547 if not self.rqexe:
1548 self.state = runQueueComplete
1549 return
1550
1551 if now:
1552 self.rqexe.finish_now()
1553 else:
1554 self.rqexe.finish()
1555
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001556 def rq_dump_sigfn(self, fn, options):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001557 bb_cache = bb.cache.NoCache(self.cooker.databuilder)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001558 mc = bb.runqueue.mc_from_tid(fn)
1559 the_data = bb_cache.loadDataFull(fn, self.cooker.collections[mc].get_file_appends(fn))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001560 siggen = bb.parse.siggen
1561 dataCaches = self.rqdata.dataCaches
1562 siggen.dump_sigfn(fn, dataCaches, options)
1563
1564 def dump_signatures(self, options):
1565 fns = set()
1566 bb.note("Reparsing files to collect dependency data")
1567
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001568 for tid in self.rqdata.runtaskentries:
1569 fn = fn_from_tid(tid)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001570 fns.add(fn)
1571
1572 max_process = int(self.cfgData.getVar("BB_NUMBER_PARSE_THREADS") or os.cpu_count() or 1)
1573 # We cannot use the real multiprocessing.Pool easily due to some local data
1574 # that can't be pickled. This is a cheap multi-process solution.
1575 launched = []
1576 while fns:
1577 if len(launched) < max_process:
1578 p = Process(target=self.rq_dump_sigfn, args=(fns.pop(), options))
1579 p.start()
1580 launched.append(p)
1581 for q in launched:
1582 # The finished processes are joined when calling is_alive()
1583 if not q.is_alive():
1584 launched.remove(q)
1585 for p in launched:
1586 p.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001587
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001588 bb.parse.siggen.dump_sigs(self.rqdata.dataCaches, options)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001589
1590 return
1591
1592 def print_diffscenetasks(self):
1593
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001594 noexec = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001595 tocheck = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001596
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001597 for tid in self.rqdata.runtaskentries:
1598 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1599 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001600
1601 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001602 noexec.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001603 continue
1604
Brad Bishop96ff1982019-08-19 13:50:42 -04001605 tocheck.add(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001606
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001607 valid_new = self.validate_hashes(tocheck, self.cooker.data, 0, True, summary=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001608
1609 # Tasks which are both setscene and noexec never care about dependencies
1610 # We therefore find tasks which are setscene and noexec and mark their
1611 # unique dependencies as valid.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001612 for tid in noexec:
1613 if tid not in self.rqdata.runq_setscene_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001614 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001615 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001616 hasnoexecparents = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001617 for dep2 in self.rqdata.runtaskentries[dep].revdeps:
1618 if dep2 in self.rqdata.runq_setscene_tids and dep2 in noexec:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001619 continue
1620 hasnoexecparents = False
1621 break
1622 if hasnoexecparents:
1623 valid_new.add(dep)
1624
1625 invalidtasks = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001626 for tid in self.rqdata.runtaskentries:
1627 if tid not in valid_new and tid not in noexec:
1628 invalidtasks.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001629
1630 found = set()
1631 processed = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001632 for tid in invalidtasks:
1633 toprocess = set([tid])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001634 while toprocess:
1635 next = set()
1636 for t in toprocess:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001637 for dep in self.rqdata.runtaskentries[t].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001638 if dep in invalidtasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001639 found.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001640 if dep not in processed:
1641 processed.add(dep)
1642 next.add(dep)
1643 toprocess = next
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001644 if tid in found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001645 toprocess = set()
1646
1647 tasklist = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001648 for tid in invalidtasks.difference(found):
1649 tasklist.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001650
1651 if tasklist:
1652 bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
1653
1654 return invalidtasks.difference(found)
1655
1656 def write_diffscenetasks(self, invalidtasks):
1657
1658 # Define recursion callback
1659 def recursecb(key, hash1, hash2):
1660 hashes = [hash1, hash2]
1661 hashfiles = bb.siggen.find_siginfo(key, None, hashes, self.cfgData)
1662
1663 recout = []
1664 if len(hashfiles) == 2:
1665 out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
Brad Bishopc342db32019-05-15 21:57:59 -04001666 recout.extend(list(' ' + l for l in out2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001667 else:
1668 recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
1669
1670 return recout
1671
1672
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001673 for tid in invalidtasks:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001674 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1675 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001676 h = self.rqdata.runtaskentries[tid].hash
Patrick Williams03907ee2022-05-01 06:28:52 -05001677 matches = bb.siggen.find_siginfo(pn, taskname, [], self.cooker.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001678 match = None
1679 for m in matches:
1680 if h in m:
1681 match = m
1682 if match is None:
1683 bb.fatal("Can't find a task we're supposed to have written out? (hash: %s)?" % h)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001684 matches = {k : v for k, v in iter(matches.items()) if h not in k}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001685 if matches:
1686 latestmatch = sorted(matches.keys(), key=lambda f: matches[f])[-1]
Brad Bishop19323692019-04-05 15:28:33 -04001687 prevh = __find_sha256__.search(latestmatch).group(0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001688 output = bb.siggen.compare_sigfiles(latestmatch, match, recursecb)
1689 bb.plain("\nTask %s:%s couldn't be used from the cache because:\n We need hash %s, closest matching task was %s\n " % (pn, taskname, h, prevh) + '\n '.join(output))
1690
Brad Bishop96ff1982019-08-19 13:50:42 -04001691
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001692class RunQueueExecute:
1693
1694 def __init__(self, rq):
1695 self.rq = rq
1696 self.cooker = rq.cooker
1697 self.cfgData = rq.cfgData
1698 self.rqdata = rq.rqdata
1699
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001700 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
1701 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001702
Brad Bishop96ff1982019-08-19 13:50:42 -04001703 self.sq_buildable = set()
1704 self.sq_running = set()
1705 self.sq_live = set()
1706
Brad Bishop08902b02019-08-20 09:16:51 -04001707 self.updated_taskhash_queue = []
1708 self.pending_migrations = set()
1709
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001710 self.runq_buildable = set()
1711 self.runq_running = set()
1712 self.runq_complete = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001713 self.runq_tasksrun = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001714
1715 self.build_stamps = {}
1716 self.build_stamps2 = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001717 self.failed_tids = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001718 self.sq_deferred = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001719
1720 self.stampcache = {}
1721
Brad Bishop08902b02019-08-20 09:16:51 -04001722 self.holdoff_tasks = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04001723 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04001724 self.sqdone = False
1725
Andrew Geissler5199d832021-09-24 16:47:35 -05001726 self.stats = RunQueueStats(len(self.rqdata.runtaskentries), len(self.rqdata.runq_setscene_tids))
Brad Bishop96ff1982019-08-19 13:50:42 -04001727
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001728 for mc in rq.worker:
1729 rq.worker[mc].pipe.setrunqueueexec(self)
1730 for mc in rq.fakeworker:
1731 rq.fakeworker[mc].pipe.setrunqueueexec(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001732
1733 if self.number_tasks <= 0:
1734 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks)
1735
Brad Bishop96ff1982019-08-19 13:50:42 -04001736 # List of setscene tasks which we've covered
1737 self.scenequeue_covered = set()
1738 # List of tasks which are covered (including setscene ones)
1739 self.tasks_covered = set()
1740 self.tasks_scenequeue_done = set()
1741 self.scenequeue_notcovered = set()
1742 self.tasks_notcovered = set()
1743 self.scenequeue_notneeded = set()
1744
Brad Bishop08902b02019-08-20 09:16:51 -04001745 # We can't skip specified target tasks which aren't setscene tasks
1746 self.cantskip = set(self.rqdata.target_tids)
1747 self.cantskip.difference_update(self.rqdata.runq_setscene_tids)
1748 self.cantskip.intersection_update(self.rqdata.runtaskentries)
Brad Bishop96ff1982019-08-19 13:50:42 -04001749
1750 schedulers = self.get_schedulers()
1751 for scheduler in schedulers:
1752 if self.scheduler == scheduler.name:
1753 self.sched = scheduler(self, self.rqdata)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001754 logger.debug("Using runqueue scheduler '%s'", scheduler.name)
Brad Bishop96ff1982019-08-19 13:50:42 -04001755 break
1756 else:
1757 bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
1758 (self.scheduler, ", ".join(obj.name for obj in schedulers)))
1759
Andrew Geissler595f6302022-01-24 19:11:47 +00001760 #if self.rqdata.runq_setscene_tids:
Brad Bishop08902b02019-08-20 09:16:51 -04001761 self.sqdata = SQData()
1762 build_scenequeue_data(self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self)
Brad Bishop96ff1982019-08-19 13:50:42 -04001763
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001764 def runqueue_process_waitpid(self, task, status, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001765
1766 # self.build_stamps[pid] may not exist when use shared work directory.
1767 if task in self.build_stamps:
1768 self.build_stamps2.remove(self.build_stamps[task])
1769 del self.build_stamps[task]
1770
Brad Bishop96ff1982019-08-19 13:50:42 -04001771 if task in self.sq_live:
1772 if status != 0:
1773 self.sq_task_fail(task, status)
1774 else:
1775 self.sq_task_complete(task)
1776 self.sq_live.remove(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05001777 self.stats.updateActiveSetscene(len(self.sq_live))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001778 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04001779 if status != 0:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001780 self.task_fail(task, status, fakerootlog=fakerootlog)
Brad Bishop96ff1982019-08-19 13:50:42 -04001781 else:
1782 self.task_complete(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001783 return True
1784
1785 def finish_now(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001786 for mc in self.rq.worker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001787 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001788 self.rq.worker[mc].process.stdin.write(b"<finishnow></finishnow>")
1789 self.rq.worker[mc].process.stdin.flush()
1790 except IOError:
1791 # worker must have died?
1792 pass
1793 for mc in self.rq.fakeworker:
1794 try:
1795 self.rq.fakeworker[mc].process.stdin.write(b"<finishnow></finishnow>")
1796 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001797 except IOError:
1798 # worker must have died?
1799 pass
1800
Andrew Geissler595f6302022-01-24 19:11:47 +00001801 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001802 self.rq.state = runQueueFailed
1803 return
1804
1805 self.rq.state = runQueueComplete
1806 return
1807
1808 def finish(self):
1809 self.rq.state = runQueueCleanUp
1810
Andrew Geissler5199d832021-09-24 16:47:35 -05001811 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001812 if active > 0:
1813 bb.event.fire(runQueueExitWait(active), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001814 self.rq.read_workers()
1815 return self.rq.active_fds()
1816
Andrew Geissler595f6302022-01-24 19:11:47 +00001817 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001818 self.rq.state = runQueueFailed
1819 return True
1820
1821 self.rq.state = runQueueComplete
1822 return True
1823
Brad Bishop96ff1982019-08-19 13:50:42 -04001824 # Used by setscene only
1825 def check_dependencies(self, task, taskdeps):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001826 if not self.rq.depvalidate:
1827 return False
1828
Brad Bishop08902b02019-08-20 09:16:51 -04001829 # Must not edit parent data
1830 taskdeps = set(taskdeps)
1831
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001832 taskdata = {}
1833 taskdeps.add(task)
1834 for dep in taskdeps:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001835 (mc, fn, taskname, taskfn) = split_tid_mcfn(dep)
1836 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001837 taskdata[dep] = [pn, taskname, fn]
1838 call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001839 locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001840 valid = bb.utils.better_eval(call, locs)
1841 return valid
1842
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001843 def can_start_task(self):
Andrew Geissler5199d832021-09-24 16:47:35 -05001844 active = self.stats.active + len(self.sq_live)
Brad Bishop96ff1982019-08-19 13:50:42 -04001845 can_start = active < self.number_tasks
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001846 return can_start
1847
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001848 def get_schedulers(self):
1849 schedulers = set(obj for obj in globals().values()
1850 if type(obj) is type and
1851 issubclass(obj, RunQueueScheduler))
1852
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001853 user_schedulers = self.cfgData.getVar("BB_SCHEDULERS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001854 if user_schedulers:
1855 for sched in user_schedulers.split():
1856 if not "." in sched:
1857 bb.note("Ignoring scheduler '%s' from BB_SCHEDULERS: not an import" % sched)
1858 continue
1859
1860 modname, name = sched.rsplit(".", 1)
1861 try:
1862 module = __import__(modname, fromlist=(name,))
1863 except ImportError as exc:
1864 logger.critical("Unable to import scheduler '%s' from '%s': %s" % (name, modname, exc))
1865 raise SystemExit(1)
1866 else:
1867 schedulers.add(getattr(module, name))
1868 return schedulers
1869
1870 def setbuildable(self, task):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001871 self.runq_buildable.add(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001872 self.sched.newbuildable(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001873
1874 def task_completeoutright(self, task):
1875 """
1876 Mark a task as completed
1877 Look at the reverse dependencies and mark any task with
1878 completed dependencies as buildable
1879 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001880 self.runq_complete.add(task)
1881 for revdep in self.rqdata.runtaskentries[task].revdeps:
1882 if revdep in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001883 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001884 if revdep in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001885 continue
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001886 alldeps = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001887 for dep in self.rqdata.runtaskentries[revdep].depends:
1888 if dep not in self.runq_complete:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001889 alldeps = False
1890 break
1891 if alldeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001892 self.setbuildable(revdep)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001893 logger.debug("Marking task %s as buildable", revdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001894
Andrew Geissler5199d832021-09-24 16:47:35 -05001895 for t in self.sq_deferred.copy():
1896 if self.sq_deferred[t] == task:
1897 logger.debug2("Deferred task %s now buildable" % t)
1898 del self.sq_deferred[t]
1899 update_scenequeue_data([t], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
1900
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001901 def task_complete(self, task):
1902 self.stats.taskCompleted()
1903 bb.event.fire(runQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
1904 self.task_completeoutright(task)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001905 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001906
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001907 def task_fail(self, task, exitcode, fakerootlog=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001908 """
1909 Called when a task has failed
1910 Updates the state engine with the failure
1911 """
1912 self.stats.taskFailed()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001913 self.failed_tids.append(task)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001914
Andrew Geissler595f6302022-01-24 19:11:47 +00001915 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001916 if fakerootlog and os.path.exists(fakerootlog):
1917 with open(fakerootlog) as fakeroot_log_file:
1918 fakeroot_failed = False
1919 for line in reversed(fakeroot_log_file.readlines()):
1920 for fakeroot_error in ['mismatch', 'error', 'fatal']:
1921 if fakeroot_error in line.lower():
1922 fakeroot_failed = True
1923 if 'doing new pid setup and server start' in line:
1924 break
Andrew Geissler595f6302022-01-24 19:11:47 +00001925 fakeroot_log.append(line)
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001926
1927 if not fakeroot_failed:
Andrew Geissler595f6302022-01-24 19:11:47 +00001928 fakeroot_log = []
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001929
Andrew Geissler595f6302022-01-24 19:11:47 +00001930 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 -05001931
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001932 if self.rqdata.taskData[''].halt:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001933 self.rq.state = runQueueCleanUp
1934
1935 def task_skip(self, task, reason):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001936 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001937 self.setbuildable(task)
1938 bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
1939 self.task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001940 self.stats.taskSkipped()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001941 self.stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001942
Brad Bishop08902b02019-08-20 09:16:51 -04001943 def summarise_scenequeue_errors(self):
1944 err = False
1945 if not self.sqdone:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001946 logger.debug('We could skip tasks %s', "\n".join(sorted(self.scenequeue_covered)))
Andrew Geissler5199d832021-09-24 16:47:35 -05001947 completeevent = sceneQueueComplete(self.stats, self.rq)
Brad Bishop08902b02019-08-20 09:16:51 -04001948 bb.event.fire(completeevent, self.cfgData)
1949 if self.sq_deferred:
1950 logger.error("Scenequeue had deferred entries: %s" % pprint.pformat(self.sq_deferred))
1951 err = True
1952 if self.updated_taskhash_queue:
1953 logger.error("Scenequeue had unprocessed changed taskhash entries: %s" % pprint.pformat(self.updated_taskhash_queue))
1954 err = True
1955 if self.holdoff_tasks:
1956 logger.error("Scenequeue had holdoff tasks: %s" % pprint.pformat(self.holdoff_tasks))
1957 err = True
1958
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001959 for tid in self.scenequeue_covered.intersection(self.scenequeue_notcovered):
1960 # No task should end up in both covered and uncovered, that is a bug.
1961 logger.error("Setscene task %s in both covered and notcovered." % tid)
1962
Brad Bishop08902b02019-08-20 09:16:51 -04001963 for tid in self.rqdata.runq_setscene_tids:
1964 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
1965 err = True
1966 logger.error("Setscene Task %s was never marked as covered or not covered" % tid)
1967 if tid not in self.sq_buildable:
1968 err = True
1969 logger.error("Setscene Task %s was never marked as buildable" % tid)
1970 if tid not in self.sq_running:
1971 err = True
1972 logger.error("Setscene Task %s was never marked as running" % tid)
1973
1974 for x in self.rqdata.runtaskentries:
1975 if x not in self.tasks_covered and x not in self.tasks_notcovered:
1976 logger.error("Task %s was never moved from the setscene queue" % x)
1977 err = True
1978 if x not in self.tasks_scenequeue_done:
1979 logger.error("Task %s was never processed by the setscene code" % x)
1980 err = True
Andrew Geissler595f6302022-01-24 19:11:47 +00001981 if not self.rqdata.runtaskentries[x].depends and x not in self.runq_buildable:
Brad Bishop08902b02019-08-20 09:16:51 -04001982 logger.error("Task %s was never marked as buildable by the setscene code" % x)
1983 err = True
1984 return err
1985
1986
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001987 def execute(self):
1988 """
Brad Bishop96ff1982019-08-19 13:50:42 -04001989 Run the tasks in a queue prepared by prepare_runqueue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001990 """
1991
1992 self.rq.read_workers()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001993 if self.updated_taskhash_queue or self.pending_migrations:
1994 self.process_possible_migrations()
1995
1996 if not hasattr(self, "sorted_setscene_tids"):
1997 # Don't want to sort this set every execution
1998 self.sorted_setscene_tids = sorted(self.rqdata.runq_setscene_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001999
Brad Bishop96ff1982019-08-19 13:50:42 -04002000 task = None
2001 if not self.sqdone and self.can_start_task():
2002 # Find the next setscene to run
Andrew Geissler82c905d2020-04-13 13:39:40 -05002003 for nexttask in self.sorted_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002004 if nexttask in self.sq_buildable and nexttask not in self.sq_running and self.sqdata.stamps[nexttask] not in self.build_stamps.values():
Andrew Geissler595f6302022-01-24 19:11:47 +00002005 if nexttask not in self.sqdata.unskippable and self.sqdata.sq_revdeps[nexttask] and self.sqdata.sq_revdeps[nexttask].issubset(self.scenequeue_covered) and self.check_dependencies(nexttask, self.sqdata.sq_revdeps[nexttask]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002006 if nexttask not in self.rqdata.target_tids:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002007 logger.debug2("Skipping setscene for task %s" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002008 self.sq_task_skip(nexttask)
2009 self.scenequeue_notneeded.add(nexttask)
2010 if nexttask in self.sq_deferred:
2011 del self.sq_deferred[nexttask]
2012 return True
Brad Bishop08902b02019-08-20 09:16:51 -04002013 # If covered tasks are running, need to wait for them to complete
2014 for t in self.sqdata.sq_covered_tasks[nexttask]:
2015 if t in self.runq_running and t not in self.runq_complete:
2016 continue
Brad Bishop96ff1982019-08-19 13:50:42 -04002017 if nexttask in self.sq_deferred:
2018 if self.sq_deferred[nexttask] not in self.runq_complete:
2019 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002020 logger.debug("Task %s no longer deferred" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002021 del self.sq_deferred[nexttask]
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002022 valid = self.rq.validate_hashes(set([nexttask]), self.cooker.data, 0, False, summary=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002023 if not valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002024 logger.debug("%s didn't become valid, skipping setscene" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002025 self.sq_task_failoutright(nexttask)
2026 return True
Brad Bishop96ff1982019-08-19 13:50:42 -04002027 if nexttask in self.sqdata.outrightfail:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002028 logger.debug2('No package found, so skipping setscene task %s', nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002029 self.sq_task_failoutright(nexttask)
2030 return True
2031 if nexttask in self.sqdata.unskippable:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002032 logger.debug2("Setscene task %s is unskippable" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002033 task = nexttask
2034 break
2035 if task is not None:
2036 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
2037 taskname = taskname + "_setscene"
2038 if self.rq.check_stamp_task(task, taskname_from_tid(task), recurse = True, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002039 logger.debug2('Stamp for underlying task %s is current, so skipping setscene variant', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002040 self.sq_task_failoutright(task)
2041 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002042
Brad Bishop96ff1982019-08-19 13:50:42 -04002043 if self.cooker.configuration.force:
2044 if task in self.rqdata.target_tids:
2045 self.sq_task_failoutright(task)
2046 return True
2047
2048 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002049 logger.debug2('Setscene stamp current task %s, so skip it and its dependencies', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002050 self.sq_task_skip(task)
2051 return True
2052
2053 if self.cooker.configuration.skipsetscene:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002054 logger.debug2('No setscene tasks should be executed. Skipping %s', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002055 self.sq_task_failoutright(task)
2056 return True
2057
Andrew Geissler5199d832021-09-24 16:47:35 -05002058 startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
Brad Bishop96ff1982019-08-19 13:50:42 -04002059 bb.event.fire(startevent, self.cfgData)
2060
2061 taskdepdata = self.sq_build_taskdepdata(task)
2062
2063 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2064 taskhash = self.rqdata.get_task_hash(task)
2065 unihash = self.rqdata.get_task_unihash(task)
2066 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
2067 if not mc in self.rq.fakeworker:
2068 self.rq.start_fakeworker(self, mc)
Andrew Geissler5a43b432020-06-13 10:46:56 -05002069 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps((taskfn, task, taskname, taskhash, unihash, True, self.cooker.collections[mc].get_file_appends(taskfn), taskdepdata, False)) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002070 self.rq.fakeworker[mc].process.stdin.flush()
2071 else:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002072 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps((taskfn, task, taskname, taskhash, unihash, True, self.cooker.collections[mc].get_file_appends(taskfn), taskdepdata, False)) + b"</runtask>")
Brad Bishop96ff1982019-08-19 13:50:42 -04002073 self.rq.worker[mc].process.stdin.flush()
2074
2075 self.build_stamps[task] = bb.build.stampfile(taskname, self.rqdata.dataCaches[mc], taskfn, noextra=True)
2076 self.build_stamps2.append(self.build_stamps[task])
2077 self.sq_running.add(task)
2078 self.sq_live.add(task)
Andrew Geissler5199d832021-09-24 16:47:35 -05002079 self.stats.updateActiveSetscene(len(self.sq_live))
Brad Bishop96ff1982019-08-19 13:50:42 -04002080 if self.can_start_task():
2081 return True
2082
Brad Bishopc68388fc2019-08-26 01:33:31 -04002083 self.update_holdofftasks()
2084
Brad Bishop08902b02019-08-20 09:16:51 -04002085 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 -05002086 hashequiv_logger.verbose("Setscene tasks completed")
Brad Bishop96ff1982019-08-19 13:50:42 -04002087
Brad Bishop08902b02019-08-20 09:16:51 -04002088 err = self.summarise_scenequeue_errors()
Brad Bishop96ff1982019-08-19 13:50:42 -04002089 if err:
2090 self.rq.state = runQueueFailed
2091 return True
2092
2093 if self.cooker.configuration.setsceneonly:
2094 self.rq.state = runQueueComplete
2095 return True
2096 self.sqdone = True
2097
2098 if self.stats.total == 0:
2099 # nothing to do
2100 self.rq.state = runQueueComplete
2101 return True
2102
2103 if self.cooker.configuration.setsceneonly:
2104 task = None
2105 else:
2106 task = self.sched.next()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002107 if task is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002108 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002109
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002110 if self.rqdata.setscene_ignore_tasks is not None:
2111 if self.check_setscene_ignore_tasks(task):
2112 self.task_fail(task, "setscene ignore_tasks")
Brad Bishop96ff1982019-08-19 13:50:42 -04002113 return True
2114
2115 if task in self.tasks_covered:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002116 logger.debug2("Setscene covered task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002117 self.task_skip(task, "covered")
2118 return True
2119
2120 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002121 logger.debug2("Stamp current task %s", task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002122
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002123 self.task_skip(task, "existing")
Andrew Geissler82c905d2020-04-13 13:39:40 -05002124 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002125 return True
2126
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002127 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002128 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2129 startevent = runQueueTaskStarted(task, self.stats, self.rq,
2130 noexec=True)
2131 bb.event.fire(startevent, self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002132 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002133 self.stats.taskActive()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002134 if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002135 bb.build.make_stamp(taskname, self.rqdata.dataCaches[mc], taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002136 self.task_complete(task)
2137 return True
2138 else:
2139 startevent = runQueueTaskStarted(task, self.stats, self.rq)
2140 bb.event.fire(startevent, self.cfgData)
2141
2142 taskdepdata = self.build_taskdepdata(task)
2143
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002144 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Brad Bishop19323692019-04-05 15:28:33 -04002145 taskhash = self.rqdata.get_task_hash(task)
2146 unihash = self.rqdata.get_task_unihash(task)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002147 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 -05002148 if not mc in self.rq.fakeworker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002149 try:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002150 self.rq.start_fakeworker(self, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002151 except OSError as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002152 logger.critical("Failed to spawn fakeroot worker to run %s: %s" % (task, str(exc)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002153 self.rq.state = runQueueFailed
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002154 self.stats.taskFailed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002155 return True
Andrew Geissler5a43b432020-06-13 10:46:56 -05002156 self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps((taskfn, task, taskname, taskhash, unihash, False, self.cooker.collections[mc].get_file_appends(taskfn), taskdepdata, self.rqdata.setscene_enforce)) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002157 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002158 else:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002159 self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps((taskfn, task, taskname, taskhash, unihash, False, self.cooker.collections[mc].get_file_appends(taskfn), taskdepdata, self.rqdata.setscene_enforce)) + b"</runtask>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002160 self.rq.worker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002161
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002162 self.build_stamps[task] = bb.build.stampfile(taskname, self.rqdata.dataCaches[mc], taskfn, noextra=True)
2163 self.build_stamps2.append(self.build_stamps[task])
2164 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002165 self.stats.taskActive()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002166 if self.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002167 return True
2168
Andrew Geissler595f6302022-01-24 19:11:47 +00002169 if self.stats.active > 0 or self.sq_live:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002170 self.rq.read_workers()
2171 return self.rq.active_fds()
2172
Brad Bishop96ff1982019-08-19 13:50:42 -04002173 # No more tasks can be run. If we have deferred setscene tasks we should run them.
2174 if self.sq_deferred:
2175 tid = self.sq_deferred.pop(list(self.sq_deferred.keys())[0])
2176 logger.warning("Runqeueue deadlocked on deferred tasks, forcing task %s" % tid)
Andrew Geissler5199d832021-09-24 16:47:35 -05002177 if tid not in self.runq_complete:
2178 self.sq_task_failoutright(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002179 return True
2180
Andrew Geissler595f6302022-01-24 19:11:47 +00002181 if self.failed_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002182 self.rq.state = runQueueFailed
2183 return True
2184
2185 # Sanity Checks
Brad Bishop08902b02019-08-20 09:16:51 -04002186 err = self.summarise_scenequeue_errors()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002187 for task in self.rqdata.runtaskentries:
2188 if task not in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002189 logger.error("Task %s never buildable!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002190 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002191 elif task not in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002192 logger.error("Task %s never ran!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002193 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002194 elif task not in self.runq_complete:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002195 logger.error("Task %s never completed!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002196 err = True
2197
2198 if err:
2199 self.rq.state = runQueueFailed
2200 else:
2201 self.rq.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002202
2203 return True
2204
Brad Bishopc68388fc2019-08-26 01:33:31 -04002205 def filtermcdeps(self, task, mc, deps):
Andrew Geissler99467da2019-02-25 18:54:23 -06002206 ret = set()
Andrew Geissler99467da2019-02-25 18:54:23 -06002207 for dep in deps:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002208 thismc = mc_from_tid(dep)
2209 if thismc != mc:
Andrew Geissler99467da2019-02-25 18:54:23 -06002210 continue
2211 ret.add(dep)
2212 return ret
2213
Brad Bishopa34c0302019-09-23 22:34:48 -04002214 # We filter out multiconfig dependencies from taskdepdata we pass to the tasks
Andrew Geissler99467da2019-02-25 18:54:23 -06002215 # as most code can't handle them
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002216 def build_taskdepdata(self, task):
2217 taskdepdata = {}
Brad Bishopc68388fc2019-08-26 01:33:31 -04002218 mc = mc_from_tid(task)
Brad Bishop08902b02019-08-20 09:16:51 -04002219 next = self.rqdata.runtaskentries[task].depends.copy()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002220 next.add(task)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002221 next = self.filtermcdeps(task, mc, next)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002222 while next:
2223 additional = []
2224 for revdep in next:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002225 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2226 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2227 deps = self.rqdata.runtaskentries[revdep].depends
2228 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002229 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002230 unihash = self.rqdata.runtaskentries[revdep].unihash
Brad Bishopc68388fc2019-08-26 01:33:31 -04002231 deps = self.filtermcdeps(task, mc, deps)
Brad Bishop19323692019-04-05 15:28:33 -04002232 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002233 for revdep2 in deps:
2234 if revdep2 not in taskdepdata:
2235 additional.append(revdep2)
2236 next = additional
2237
2238 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2239 return taskdepdata
2240
Brad Bishop08902b02019-08-20 09:16:51 -04002241 def update_holdofftasks(self):
Brad Bishopc68388fc2019-08-26 01:33:31 -04002242
2243 if not self.holdoff_need_update:
2244 return
2245
2246 notcovered = set(self.scenequeue_notcovered)
2247 notcovered |= self.cantskip
2248 for tid in self.scenequeue_notcovered:
2249 notcovered |= self.sqdata.sq_covered_tasks[tid]
2250 notcovered |= self.sqdata.unskippable.difference(self.rqdata.runq_setscene_tids)
2251 notcovered.intersection_update(self.tasks_scenequeue_done)
2252
2253 covered = set(self.scenequeue_covered)
2254 for tid in self.scenequeue_covered:
2255 covered |= self.sqdata.sq_covered_tasks[tid]
2256 covered.difference_update(notcovered)
2257 covered.intersection_update(self.tasks_scenequeue_done)
2258
2259 for tid in notcovered | covered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002260 if not self.rqdata.runtaskentries[tid].depends:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002261 self.setbuildable(tid)
2262 elif self.rqdata.runtaskentries[tid].depends.issubset(self.runq_complete):
2263 self.setbuildable(tid)
2264
2265 self.tasks_covered = covered
2266 self.tasks_notcovered = notcovered
2267
Brad Bishop08902b02019-08-20 09:16:51 -04002268 self.holdoff_tasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002269
Brad Bishop08902b02019-08-20 09:16:51 -04002270 for tid in self.rqdata.runq_setscene_tids:
2271 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2272 self.holdoff_tasks.add(tid)
2273
2274 for tid in self.holdoff_tasks.copy():
2275 for dep in self.sqdata.sq_covered_tasks[tid]:
2276 if dep not in self.runq_complete:
2277 self.holdoff_tasks.add(dep)
2278
Brad Bishopc68388fc2019-08-26 01:33:31 -04002279 self.holdoff_need_update = False
2280
Brad Bishop08902b02019-08-20 09:16:51 -04002281 def process_possible_migrations(self):
2282
2283 changed = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002284 toprocess = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002285 for tid, unihash in self.updated_taskhash_queue.copy():
2286 if tid in self.runq_running and tid not in self.runq_complete:
2287 continue
2288
2289 self.updated_taskhash_queue.remove((tid, unihash))
2290
2291 if unihash != self.rqdata.runtaskentries[tid].unihash:
Andrew Geisslerc926e172021-05-07 16:11:35 -05002292 # Make sure we rehash any other tasks with the same task hash that we're deferred against.
2293 torehash = [tid]
2294 for deftid in self.sq_deferred:
2295 if self.sq_deferred[deftid] == tid:
2296 torehash.append(deftid)
2297 for hashtid in torehash:
2298 hashequiv_logger.verbose("Task %s unihash changed to %s" % (hashtid, unihash))
2299 self.rqdata.runtaskentries[hashtid].unihash = unihash
2300 bb.parse.siggen.set_unihash(hashtid, unihash)
2301 toprocess.add(hashtid)
Andrew Geissler78b72792022-06-14 06:47:25 -05002302 if torehash:
2303 # Need to save after set_unihash above
2304 bb.parse.siggen.save_unitaskhashes()
Brad Bishop08902b02019-08-20 09:16:51 -04002305
Andrew Geissler82c905d2020-04-13 13:39:40 -05002306 # Work out all tasks which depend upon these
2307 total = set()
2308 next = set()
2309 for p in toprocess:
2310 next |= self.rqdata.runtaskentries[p].revdeps
2311 while next:
2312 current = next.copy()
2313 total = total | next
2314 next = set()
2315 for ntid in current:
2316 next |= self.rqdata.runtaskentries[ntid].revdeps
2317 next.difference_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002318
Andrew Geissler82c905d2020-04-13 13:39:40 -05002319 # Now iterate those tasks in dependency order to regenerate their taskhash/unihash
2320 next = set()
2321 for p in total:
Andrew Geissler595f6302022-01-24 19:11:47 +00002322 if not self.rqdata.runtaskentries[p].depends:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002323 next.add(p)
2324 elif self.rqdata.runtaskentries[p].depends.isdisjoint(total):
2325 next.add(p)
2326
2327 # When an item doesn't have dependencies in total, we can process it. Drop items from total when handled
2328 while next:
2329 current = next.copy()
2330 next = set()
2331 for tid in current:
Andrew Geissler595f6302022-01-24 19:11:47 +00002332 if self.rqdata.runtaskentries[p].depends and not self.rqdata.runtaskentries[tid].depends.isdisjoint(total):
Andrew Geissler82c905d2020-04-13 13:39:40 -05002333 continue
2334 orighash = self.rqdata.runtaskentries[tid].hash
Andrew Geissler5a43b432020-06-13 10:46:56 -05002335 dc = bb.parse.siggen.get_data_caches(self.rqdata.dataCaches, mc_from_tid(tid))
2336 newhash = bb.parse.siggen.get_taskhash(tid, self.rqdata.runtaskentries[tid].depends, dc)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002337 origuni = self.rqdata.runtaskentries[tid].unihash
2338 newuni = bb.parse.siggen.get_unihash(tid)
2339 # FIXME, need to check it can come from sstate at all for determinism?
2340 remapped = False
2341 if newuni == origuni:
2342 # Nothing to do, we match, skip code below
2343 remapped = True
2344 elif tid in self.scenequeue_covered or tid in self.sq_live:
2345 # Already ran this setscene task or it running. Report the new taskhash
2346 bb.parse.siggen.report_unihash_equiv(tid, newhash, origuni, newuni, self.rqdata.dataCaches)
2347 hashequiv_logger.verbose("Already covered setscene for %s so ignoring rehash (remap)" % (tid))
2348 remapped = True
2349
2350 if not remapped:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002351 #logger.debug("Task %s hash changes: %s->%s %s->%s" % (tid, orighash, newhash, origuni, newuni))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002352 self.rqdata.runtaskentries[tid].hash = newhash
2353 self.rqdata.runtaskentries[tid].unihash = newuni
2354 changed.add(tid)
2355
2356 next |= self.rqdata.runtaskentries[tid].revdeps
2357 total.remove(tid)
2358 next.intersection_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002359
2360 if changed:
2361 for mc in self.rq.worker:
2362 self.rq.worker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2363 for mc in self.rq.fakeworker:
2364 self.rq.fakeworker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2365
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002366 hashequiv_logger.debug(pprint.pformat("Tasks changed:\n%s" % (changed)))
Brad Bishop08902b02019-08-20 09:16:51 -04002367
2368 for tid in changed:
2369 if tid not in self.rqdata.runq_setscene_tids:
2370 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002371 if tid not in self.pending_migrations:
2372 self.pending_migrations.add(tid)
2373
Andrew Geissler82c905d2020-04-13 13:39:40 -05002374 update_tasks = []
Brad Bishop08902b02019-08-20 09:16:51 -04002375 for tid in self.pending_migrations.copy():
Andrew Geissler82c905d2020-04-13 13:39:40 -05002376 if tid in self.runq_running or tid in self.sq_live:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002377 # Too late, task already running, not much we can do now
2378 self.pending_migrations.remove(tid)
2379 continue
2380
Brad Bishop08902b02019-08-20 09:16:51 -04002381 valid = True
2382 # Check no tasks this covers are running
2383 for dep in self.sqdata.sq_covered_tasks[tid]:
2384 if dep in self.runq_running and dep not in self.runq_complete:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002385 hashequiv_logger.debug2("Task %s is running which blocks setscene for %s from running" % (dep, tid))
Brad Bishop08902b02019-08-20 09:16:51 -04002386 valid = False
2387 break
2388 if not valid:
2389 continue
2390
2391 self.pending_migrations.remove(tid)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002392 changed = True
Brad Bishop08902b02019-08-20 09:16:51 -04002393
2394 if tid in self.tasks_scenequeue_done:
2395 self.tasks_scenequeue_done.remove(tid)
2396 for dep in self.sqdata.sq_covered_tasks[tid]:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002397 if dep in self.runq_complete and dep not in self.runq_tasksrun:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002398 bb.error("Task %s marked as completed but now needing to rerun? Halting build." % dep)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002399 self.failed_tids.append(tid)
2400 self.rq.state = runQueueCleanUp
2401 return
2402
Brad Bishop08902b02019-08-20 09:16:51 -04002403 if dep not in self.runq_complete:
2404 if dep in self.tasks_scenequeue_done and dep not in self.sqdata.unskippable:
2405 self.tasks_scenequeue_done.remove(dep)
2406
2407 if tid in self.sq_buildable:
2408 self.sq_buildable.remove(tid)
2409 if tid in self.sq_running:
2410 self.sq_running.remove(tid)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002411 harddepfail = False
2412 for t in self.sqdata.sq_harddeps:
2413 if tid in self.sqdata.sq_harddeps[t] and t in self.scenequeue_notcovered:
2414 harddepfail = True
2415 break
2416 if not harddepfail and self.sqdata.sq_revdeps[tid].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
Brad Bishop08902b02019-08-20 09:16:51 -04002417 if tid not in self.sq_buildable:
2418 self.sq_buildable.add(tid)
Andrew Geissler595f6302022-01-24 19:11:47 +00002419 if not self.sqdata.sq_revdeps[tid]:
Brad Bishop08902b02019-08-20 09:16:51 -04002420 self.sq_buildable.add(tid)
2421
2422 if tid in self.sqdata.outrightfail:
2423 self.sqdata.outrightfail.remove(tid)
2424 if tid in self.scenequeue_notcovered:
2425 self.scenequeue_notcovered.remove(tid)
2426 if tid in self.scenequeue_covered:
2427 self.scenequeue_covered.remove(tid)
2428 if tid in self.scenequeue_notneeded:
2429 self.scenequeue_notneeded.remove(tid)
2430
2431 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2432 self.sqdata.stamps[tid] = bb.build.stampfile(taskname + "_setscene", self.rqdata.dataCaches[mc], taskfn, noextra=True)
2433
2434 if tid in self.stampcache:
2435 del self.stampcache[tid]
2436
2437 if tid in self.build_stamps:
2438 del self.build_stamps[tid]
2439
Andrew Geissler82c905d2020-04-13 13:39:40 -05002440 update_tasks.append((tid, harddepfail, tid in self.sqdata.valid))
2441
2442 if update_tasks:
Brad Bishop08902b02019-08-20 09:16:51 -04002443 self.sqdone = False
Patrick Williams213cb262021-08-07 19:21:33 -05002444 for tid in [t[0] for t in update_tasks]:
2445 h = pending_hash_index(tid, self.rqdata)
2446 if h in self.sqdata.hashes and tid != self.sqdata.hashes[h]:
2447 self.sq_deferred[tid] = self.sqdata.hashes[h]
2448 bb.note("Deferring %s after %s" % (tid, self.sqdata.hashes[h]))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002449 update_scenequeue_data([t[0] for t in update_tasks], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
2450
2451 for (tid, harddepfail, origvalid) in update_tasks:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002452 if tid in self.sqdata.valid and not origvalid:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002453 hashequiv_logger.verbose("Setscene task %s became valid" % tid)
2454 if harddepfail:
2455 self.sq_task_failoutright(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002456
2457 if changed:
Andrew Geissler5199d832021-09-24 16:47:35 -05002458 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002459 self.holdoff_need_update = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002460
Brad Bishop96ff1982019-08-19 13:50:42 -04002461 def scenequeue_updatecounters(self, task, fail=False):
Brad Bishop08902b02019-08-20 09:16:51 -04002462
2463 for dep in sorted(self.sqdata.sq_deps[task]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002464 if fail and task in self.sqdata.sq_harddeps and dep in self.sqdata.sq_harddeps[task]:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002465 if dep in self.scenequeue_covered or dep in self.scenequeue_notcovered:
2466 # dependency could be already processed, e.g. noexec setscene task
2467 continue
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002468 noexec, stamppresent = check_setscene_stamps(dep, self.rqdata, self.rq, self.stampcache)
2469 if noexec or stamppresent:
2470 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002471 logger.debug2("%s was unavailable and is a hard dependency of %s so skipping" % (task, dep))
Brad Bishop96ff1982019-08-19 13:50:42 -04002472 self.sq_task_failoutright(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002473 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002474 if self.sqdata.sq_revdeps[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2475 if dep not in self.sq_buildable:
2476 self.sq_buildable.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002477
Brad Bishop96ff1982019-08-19 13:50:42 -04002478 next = set([task])
2479 while next:
2480 new = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002481 for t in sorted(next):
Brad Bishop96ff1982019-08-19 13:50:42 -04002482 self.tasks_scenequeue_done.add(t)
2483 # Look down the dependency chain for non-setscene things which this task depends on
2484 # and mark as 'done'
2485 for dep in self.rqdata.runtaskentries[t].depends:
2486 if dep in self.rqdata.runq_setscene_tids or dep in self.tasks_scenequeue_done:
2487 continue
2488 if self.rqdata.runtaskentries[dep].revdeps.issubset(self.tasks_scenequeue_done):
2489 new.add(dep)
Brad Bishop96ff1982019-08-19 13:50:42 -04002490 next = new
2491
Andrew Geissler5199d832021-09-24 16:47:35 -05002492 self.stats.updateCovered(len(self.scenequeue_covered), len(self.scenequeue_notcovered))
Brad Bishopc68388fc2019-08-26 01:33:31 -04002493 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002494
2495 def sq_task_completeoutright(self, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002496 """
2497 Mark a task as completed
2498 Look at the reverse dependencies and mark any task with
2499 completed dependencies as buildable
2500 """
2501
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002502 logger.debug('Found task %s which could be accelerated', task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002503 self.scenequeue_covered.add(task)
2504 self.scenequeue_updatecounters(task)
2505
Brad Bishop96ff1982019-08-19 13:50:42 -04002506 def sq_check_taskfail(self, task):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002507 if self.rqdata.setscene_ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002508 realtask = task.split('_setscene')[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002509 (mc, fn, taskname, taskfn) = split_tid_mcfn(realtask)
2510 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002511 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002512 logger.error('Task %s.%s failed' % (pn, taskname + "_setscene"))
2513 self.rq.state = runQueueCleanUp
2514
Brad Bishop96ff1982019-08-19 13:50:42 -04002515 def sq_task_complete(self, task):
Andrew Geissler5199d832021-09-24 16:47:35 -05002516 bb.event.fire(sceneQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
Brad Bishop96ff1982019-08-19 13:50:42 -04002517 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002518
Brad Bishop96ff1982019-08-19 13:50:42 -04002519 def sq_task_fail(self, task, result):
Andrew Geissler5199d832021-09-24 16:47:35 -05002520 bb.event.fire(sceneQueueTaskFailed(task, self.stats, result, self), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002521 self.scenequeue_notcovered.add(task)
2522 self.scenequeue_updatecounters(task, True)
Brad Bishop96ff1982019-08-19 13:50:42 -04002523 self.sq_check_taskfail(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002524
Brad Bishop96ff1982019-08-19 13:50:42 -04002525 def sq_task_failoutright(self, task):
2526 self.sq_running.add(task)
2527 self.sq_buildable.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002528 self.scenequeue_notcovered.add(task)
2529 self.scenequeue_updatecounters(task, True)
2530
Brad Bishop96ff1982019-08-19 13:50:42 -04002531 def sq_task_skip(self, task):
2532 self.sq_running.add(task)
2533 self.sq_buildable.add(task)
2534 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002535
Brad Bishop96ff1982019-08-19 13:50:42 -04002536 def sq_build_taskdepdata(self, task):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002537 def getsetscenedeps(tid):
2538 deps = set()
2539 (mc, fn, taskname, _) = split_tid_mcfn(tid)
2540 realtid = tid + "_setscene"
2541 idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
2542 for (depname, idependtask) in idepends:
2543 if depname not in self.rqdata.taskData[mc].build_targets:
2544 continue
2545
2546 depfn = self.rqdata.taskData[mc].build_targets[depname][0]
2547 if depfn is None:
2548 continue
2549 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2550 deps.add(deptid)
2551 return deps
2552
2553 taskdepdata = {}
2554 next = getsetscenedeps(task)
2555 next.add(task)
2556 while next:
2557 additional = []
2558 for revdep in next:
2559 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2560 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2561 deps = getsetscenedeps(revdep)
2562 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2563 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002564 unihash = self.rqdata.runtaskentries[revdep].unihash
2565 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002566 for revdep2 in deps:
2567 if revdep2 not in taskdepdata:
2568 additional.append(revdep2)
2569 next = additional
2570
2571 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2572 return taskdepdata
2573
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002574 def check_setscene_ignore_tasks(self, tid):
2575 # Check task that is going to run against the ignore tasks list
Brad Bishop96ff1982019-08-19 13:50:42 -04002576 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2577 # Ignore covered tasks
2578 if tid in self.tasks_covered:
2579 return False
2580 # Ignore stamped tasks
2581 if self.rq.check_stamp_task(tid, taskname, cache=self.stampcache):
2582 return False
2583 # Ignore noexec tasks
2584 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2585 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2586 return False
2587
2588 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002589 if not check_setscene_enforce_ignore_tasks(pn, taskname, self.rqdata.setscene_ignore_tasks):
Brad Bishop96ff1982019-08-19 13:50:42 -04002590 if tid in self.rqdata.runq_setscene_tids:
Andrew Geissler595f6302022-01-24 19:11:47 +00002591 msg = ['Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)]
Brad Bishop96ff1982019-08-19 13:50:42 -04002592 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002593 msg = ['Task %s.%s attempted to execute unexpectedly' % (pn, taskname)]
Andrew Geissler82c905d2020-04-13 13:39:40 -05002594 for t in self.scenequeue_notcovered:
Andrew Geissler595f6302022-01-24 19:11:47 +00002595 msg.append("\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash))
2596 msg.append('\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered))
2597 logger.error("".join(msg))
Brad Bishop96ff1982019-08-19 13:50:42 -04002598 return True
2599 return False
2600
2601class SQData(object):
2602 def __init__(self):
2603 # SceneQueue dependencies
2604 self.sq_deps = {}
2605 # SceneQueue reverse dependencies
2606 self.sq_revdeps = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002607 # Injected inter-setscene task dependencies
2608 self.sq_harddeps = {}
2609 # Cache of stamp files so duplicates can't run in parallel
2610 self.stamps = {}
2611 # Setscene tasks directly depended upon by the build
2612 self.unskippable = set()
2613 # List of setscene tasks which aren't present
2614 self.outrightfail = set()
2615 # A list of normal tasks a setscene task covers
2616 self.sq_covered_tasks = {}
2617
2618def build_scenequeue_data(sqdata, rqdata, rq, cooker, stampcache, sqrq):
2619
2620 sq_revdeps = {}
2621 sq_revdeps_squash = {}
2622 sq_collated_deps = {}
2623
2624 # We need to construct a dependency graph for the setscene functions. Intermediate
2625 # dependencies between the setscene tasks only complicate the code. This code
2626 # therefore aims to collapse the huge runqueue dependency tree into a smaller one
2627 # only containing the setscene functions.
2628
2629 rqdata.init_progress_reporter.next_stage()
2630
2631 # First process the chains up to the first setscene task.
2632 endpoints = {}
2633 for tid in rqdata.runtaskentries:
2634 sq_revdeps[tid] = copy.copy(rqdata.runtaskentries[tid].revdeps)
2635 sq_revdeps_squash[tid] = set()
Andrew Geissler595f6302022-01-24 19:11:47 +00002636 if not sq_revdeps[tid] and tid not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002637 #bb.warn("Added endpoint %s" % (tid))
2638 endpoints[tid] = set()
2639
2640 rqdata.init_progress_reporter.next_stage()
2641
2642 # Secondly process the chains between setscene tasks.
2643 for tid in rqdata.runq_setscene_tids:
2644 sq_collated_deps[tid] = set()
2645 #bb.warn("Added endpoint 2 %s" % (tid))
2646 for dep in rqdata.runtaskentries[tid].depends:
2647 if tid in sq_revdeps[dep]:
2648 sq_revdeps[dep].remove(tid)
2649 if dep not in endpoints:
2650 endpoints[dep] = set()
2651 #bb.warn(" Added endpoint 3 %s" % (dep))
2652 endpoints[dep].add(tid)
2653
2654 rqdata.init_progress_reporter.next_stage()
2655
2656 def process_endpoints(endpoints):
2657 newendpoints = {}
2658 for point, task in endpoints.items():
2659 tasks = set()
2660 if task:
2661 tasks |= task
2662 if sq_revdeps_squash[point]:
2663 tasks |= sq_revdeps_squash[point]
2664 if point not in rqdata.runq_setscene_tids:
2665 for t in tasks:
2666 sq_collated_deps[t].add(point)
2667 sq_revdeps_squash[point] = set()
2668 if point in rqdata.runq_setscene_tids:
2669 sq_revdeps_squash[point] = tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002670 continue
2671 for dep in rqdata.runtaskentries[point].depends:
2672 if point in sq_revdeps[dep]:
2673 sq_revdeps[dep].remove(point)
2674 if tasks:
2675 sq_revdeps_squash[dep] |= tasks
Andrew Geissler595f6302022-01-24 19:11:47 +00002676 if not sq_revdeps[dep] and dep not in rqdata.runq_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04002677 newendpoints[dep] = task
Andrew Geissler595f6302022-01-24 19:11:47 +00002678 if newendpoints:
Brad Bishop96ff1982019-08-19 13:50:42 -04002679 process_endpoints(newendpoints)
2680
2681 process_endpoints(endpoints)
2682
2683 rqdata.init_progress_reporter.next_stage()
2684
Brad Bishop08902b02019-08-20 09:16:51 -04002685 # Build a list of tasks which are "unskippable"
2686 # These are direct endpoints referenced by the build upto and including setscene tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002687 # Take the build endpoints (no revdeps) and find the sstate tasks they depend upon
2688 new = True
2689 for tid in rqdata.runtaskentries:
Andrew Geissler595f6302022-01-24 19:11:47 +00002690 if not rqdata.runtaskentries[tid].revdeps:
Brad Bishop96ff1982019-08-19 13:50:42 -04002691 sqdata.unskippable.add(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002692 sqdata.unskippable |= sqrq.cantskip
Brad Bishop96ff1982019-08-19 13:50:42 -04002693 while new:
2694 new = False
Brad Bishop08902b02019-08-20 09:16:51 -04002695 orig = sqdata.unskippable.copy()
2696 for tid in sorted(orig, reverse=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04002697 if tid in rqdata.runq_setscene_tids:
2698 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00002699 if not rqdata.runtaskentries[tid].depends:
Brad Bishop96ff1982019-08-19 13:50:42 -04002700 # These are tasks which have no setscene tasks in their chain, need to mark as directly buildable
Brad Bishop96ff1982019-08-19 13:50:42 -04002701 sqrq.setbuildable(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002702 sqdata.unskippable |= rqdata.runtaskentries[tid].depends
Brad Bishop08902b02019-08-20 09:16:51 -04002703 if sqdata.unskippable != orig:
2704 new = True
2705
2706 sqrq.tasks_scenequeue_done |= sqdata.unskippable.difference(rqdata.runq_setscene_tids)
Brad Bishop96ff1982019-08-19 13:50:42 -04002707
2708 rqdata.init_progress_reporter.next_stage(len(rqdata.runtaskentries))
2709
2710 # Sanity check all dependencies could be changed to setscene task references
2711 for taskcounter, tid in enumerate(rqdata.runtaskentries):
2712 if tid in rqdata.runq_setscene_tids:
2713 pass
Andrew Geissler595f6302022-01-24 19:11:47 +00002714 elif sq_revdeps_squash[tid]:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002715 bb.msg.fatal("RunQueue", "Something went badly wrong during scenequeue generation, halting. Please report this problem.")
Brad Bishop96ff1982019-08-19 13:50:42 -04002716 else:
2717 del sq_revdeps_squash[tid]
2718 rqdata.init_progress_reporter.update(taskcounter)
2719
2720 rqdata.init_progress_reporter.next_stage()
2721
2722 # Resolve setscene inter-task dependencies
2723 # e.g. do_sometask_setscene[depends] = "targetname:do_someothertask_setscene"
2724 # Note that anything explicitly depended upon will have its reverse dependencies removed to avoid circular dependencies
2725 for tid in rqdata.runq_setscene_tids:
2726 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2727 realtid = tid + "_setscene"
2728 idepends = rqdata.taskData[mc].taskentries[realtid].idepends
2729 sqdata.stamps[tid] = bb.build.stampfile(taskname + "_setscene", rqdata.dataCaches[mc], taskfn, noextra=True)
2730 for (depname, idependtask) in idepends:
2731
2732 if depname not in rqdata.taskData[mc].build_targets:
2733 continue
2734
2735 depfn = rqdata.taskData[mc].build_targets[depname][0]
2736 if depfn is None:
2737 continue
2738 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2739 if deptid not in rqdata.runtaskentries:
2740 bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (realtid, depfn, idependtask))
2741
2742 if not deptid in sqdata.sq_harddeps:
2743 sqdata.sq_harddeps[deptid] = set()
2744 sqdata.sq_harddeps[deptid].add(tid)
2745
2746 sq_revdeps_squash[tid].add(deptid)
2747 # Have to zero this to avoid circular dependencies
2748 sq_revdeps_squash[deptid] = set()
2749
2750 rqdata.init_progress_reporter.next_stage()
2751
2752 for task in sqdata.sq_harddeps:
2753 for dep in sqdata.sq_harddeps[task]:
2754 sq_revdeps_squash[dep].add(task)
2755
2756 rqdata.init_progress_reporter.next_stage()
2757
2758 #for tid in sq_revdeps_squash:
2759 # data = ""
2760 # for dep in sq_revdeps_squash[tid]:
2761 # data = data + "\n %s" % dep
2762 # bb.warn("Task %s_setscene: is %s " % (tid, data))
2763
2764 sqdata.sq_revdeps = sq_revdeps_squash
Brad Bishop96ff1982019-08-19 13:50:42 -04002765 sqdata.sq_covered_tasks = sq_collated_deps
2766
2767 # Build reverse version of revdeps to populate deps structure
2768 for tid in sqdata.sq_revdeps:
2769 sqdata.sq_deps[tid] = set()
2770 for tid in sqdata.sq_revdeps:
2771 for dep in sqdata.sq_revdeps[tid]:
2772 sqdata.sq_deps[dep].add(tid)
2773
2774 rqdata.init_progress_reporter.next_stage()
2775
Brad Bishop00e122a2019-10-05 11:10:57 -04002776 sqdata.multiconfigs = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002777 for tid in sqdata.sq_revdeps:
Brad Bishop00e122a2019-10-05 11:10:57 -04002778 sqdata.multiconfigs.add(mc_from_tid(tid))
Andrew Geissler595f6302022-01-24 19:11:47 +00002779 if not sqdata.sq_revdeps[tid]:
Brad Bishop96ff1982019-08-19 13:50:42 -04002780 sqrq.sq_buildable.add(tid)
2781
2782 rqdata.init_progress_reporter.finish()
2783
Brad Bishop00e122a2019-10-05 11:10:57 -04002784 sqdata.noexec = set()
2785 sqdata.stamppresent = set()
2786 sqdata.valid = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002787
Patrick Williams213cb262021-08-07 19:21:33 -05002788 sqdata.hashes = {}
2789 sqrq.sq_deferred = {}
2790 for mc in sorted(sqdata.multiconfigs):
2791 for tid in sorted(sqdata.sq_revdeps):
2792 if mc_from_tid(tid) != mc:
2793 continue
2794 h = pending_hash_index(tid, rqdata)
2795 if h not in sqdata.hashes:
2796 sqdata.hashes[h] = tid
2797 else:
2798 sqrq.sq_deferred[tid] = sqdata.hashes[h]
2799 bb.note("Deferring %s after %s" % (tid, sqdata.hashes[h]))
2800
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002801 update_scenequeue_data(sqdata.sq_revdeps, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002802
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002803 # Compute a list of 'stale' sstate tasks where the current hash does not match the one
2804 # in any stamp files. Pass the list out to metadata as an event.
2805 found = {}
2806 for tid in rqdata.runq_setscene_tids:
2807 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2808 stamps = bb.build.find_stale_stamps(taskname, rqdata.dataCaches[mc], taskfn)
2809 if stamps:
2810 if mc not in found:
2811 found[mc] = {}
2812 found[mc][tid] = stamps
2813 for mc in found:
2814 event = bb.event.StaleSetSceneTasks(found[mc])
2815 bb.event.fire(event, cooker.databuilder.mcdata[mc])
2816
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002817def check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=False):
2818
2819 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2820
2821 taskdep = rqdata.dataCaches[mc].task_deps[taskfn]
2822
2823 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2824 bb.build.make_stamp(taskname + "_setscene", rqdata.dataCaches[mc], taskfn)
2825 return True, False
2826
2827 if rq.check_stamp_task(tid, taskname + "_setscene", cache=stampcache):
2828 logger.debug2('Setscene stamp current for task %s', tid)
2829 return False, True
2830
2831 if rq.check_stamp_task(tid, taskname, recurse = True, cache=stampcache):
2832 logger.debug2('Normal stamp current for task %s', tid)
2833 return False, True
2834
2835 return False, False
2836
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002837def update_scenequeue_data(tids, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True):
Brad Bishop00e122a2019-10-05 11:10:57 -04002838
2839 tocheck = set()
2840
2841 for tid in sorted(tids):
2842 if tid in sqdata.stamppresent:
2843 sqdata.stamppresent.remove(tid)
2844 if tid in sqdata.valid:
2845 sqdata.valid.remove(tid)
Andrew Geisslerc926e172021-05-07 16:11:35 -05002846 if tid in sqdata.outrightfail:
2847 sqdata.outrightfail.remove(tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04002848
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002849 noexec, stamppresent = check_setscene_stamps(tid, rqdata, rq, stampcache, noexecstamp=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002850
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002851 if noexec:
Brad Bishop00e122a2019-10-05 11:10:57 -04002852 sqdata.noexec.add(tid)
2853 sqrq.sq_task_skip(tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04002854 continue
2855
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05002856 if stamppresent:
Brad Bishop00e122a2019-10-05 11:10:57 -04002857 sqdata.stamppresent.add(tid)
2858 sqrq.sq_task_skip(tid)
2859 continue
2860
2861 tocheck.add(tid)
2862
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002863 sqdata.valid |= rq.validate_hashes(tocheck, cooker.data, len(sqdata.stamppresent), False, summary=summary)
Brad Bishop00e122a2019-10-05 11:10:57 -04002864
Patrick Williams213cb262021-08-07 19:21:33 -05002865 for tid in tids:
2866 if tid in sqdata.stamppresent:
2867 continue
2868 if tid in sqdata.valid:
2869 continue
2870 if tid in sqdata.noexec:
2871 continue
2872 if tid in sqrq.scenequeue_covered:
2873 continue
2874 if tid in sqrq.scenequeue_notcovered:
2875 continue
2876 if tid in sqrq.sq_deferred:
2877 continue
2878 sqdata.outrightfail.add(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002879
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002880class TaskFailure(Exception):
2881 """
2882 Exception raised when a task in a runqueue fails
2883 """
2884 def __init__(self, x):
2885 self.args = x
2886
2887
2888class runQueueExitWait(bb.event.Event):
2889 """
2890 Event when waiting for task processes to exit
2891 """
2892
2893 def __init__(self, remain):
2894 self.remain = remain
2895 self.message = "Waiting for %s active tasks to finish" % remain
2896 bb.event.Event.__init__(self)
2897
2898class runQueueEvent(bb.event.Event):
2899 """
2900 Base runQueue event class
2901 """
2902 def __init__(self, task, stats, rq):
2903 self.taskid = task
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002904 self.taskstring = task
2905 self.taskname = taskname_from_tid(task)
2906 self.taskfile = fn_from_tid(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002907 self.taskhash = rq.rqdata.get_task_hash(task)
2908 self.stats = stats.copy()
2909 bb.event.Event.__init__(self)
2910
2911class sceneQueueEvent(runQueueEvent):
2912 """
2913 Base sceneQueue event class
2914 """
2915 def __init__(self, task, stats, rq, noexec=False):
2916 runQueueEvent.__init__(self, task, stats, rq)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002917 self.taskstring = task + "_setscene"
2918 self.taskname = taskname_from_tid(task) + "_setscene"
2919 self.taskfile = fn_from_tid(task)
2920 self.taskhash = rq.rqdata.get_task_hash(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002921
2922class runQueueTaskStarted(runQueueEvent):
2923 """
2924 Event notifying a task was started
2925 """
2926 def __init__(self, task, stats, rq, noexec=False):
2927 runQueueEvent.__init__(self, task, stats, rq)
2928 self.noexec = noexec
2929
2930class sceneQueueTaskStarted(sceneQueueEvent):
2931 """
2932 Event notifying a setscene task was started
2933 """
2934 def __init__(self, task, stats, rq, noexec=False):
2935 sceneQueueEvent.__init__(self, task, stats, rq)
2936 self.noexec = noexec
2937
2938class runQueueTaskFailed(runQueueEvent):
2939 """
2940 Event notifying a task failed
2941 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002942 def __init__(self, task, stats, exitcode, rq, fakeroot_log=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002943 runQueueEvent.__init__(self, task, stats, rq)
2944 self.exitcode = exitcode
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002945 self.fakeroot_log = fakeroot_log
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002946
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002947 def __str__(self):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05002948 if self.fakeroot_log:
2949 return "Task (%s) failed with exit code '%s' \nPseudo log:\n%s" % (self.taskstring, self.exitcode, self.fakeroot_log)
2950 else:
2951 return "Task (%s) failed with exit code '%s'" % (self.taskstring, self.exitcode)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002952
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002953class sceneQueueTaskFailed(sceneQueueEvent):
2954 """
2955 Event notifying a setscene task failed
2956 """
2957 def __init__(self, task, stats, exitcode, rq):
2958 sceneQueueEvent.__init__(self, task, stats, rq)
2959 self.exitcode = exitcode
2960
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002961 def __str__(self):
2962 return "Setscene task (%s) failed with exit code '%s' - real task will be run instead" % (self.taskstring, self.exitcode)
2963
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002964class sceneQueueComplete(sceneQueueEvent):
2965 """
2966 Event when all the sceneQueue tasks are complete
2967 """
2968 def __init__(self, stats, rq):
2969 self.stats = stats.copy()
2970 bb.event.Event.__init__(self)
2971
2972class runQueueTaskCompleted(runQueueEvent):
2973 """
2974 Event notifying a task completed
2975 """
2976
2977class sceneQueueTaskCompleted(sceneQueueEvent):
2978 """
2979 Event notifying a setscene task completed
2980 """
2981
2982class runQueueTaskSkipped(runQueueEvent):
2983 """
2984 Event notifying a task was skipped
2985 """
2986 def __init__(self, task, stats, rq, reason):
2987 runQueueEvent.__init__(self, task, stats, rq)
2988 self.reason = reason
2989
Brad Bishop08902b02019-08-20 09:16:51 -04002990class taskUniHashUpdate(bb.event.Event):
2991 """
2992 Base runQueue event class
2993 """
2994 def __init__(self, task, unihash):
2995 self.taskid = task
2996 self.unihash = unihash
2997 bb.event.Event.__init__(self)
2998
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002999class runQueuePipe():
3000 """
3001 Abstraction for a pipe between a worker thread and the server
3002 """
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003003 def __init__(self, pipein, pipeout, d, rq, rqexec, fakerootlogs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003004 self.input = pipein
3005 if pipeout:
3006 pipeout.close()
3007 bb.utils.nonblockingfd(self.input)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003008 self.queue = b""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003009 self.d = d
3010 self.rq = rq
3011 self.rqexec = rqexec
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003012 self.fakerootlogs = fakerootlogs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003013
3014 def setrunqueueexec(self, rqexec):
3015 self.rqexec = rqexec
3016
3017 def read(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003018 for workers, name in [(self.rq.worker, "Worker"), (self.rq.fakeworker, "Fakeroot")]:
3019 for worker in workers.values():
3020 worker.process.poll()
3021 if worker.process.returncode is not None and not self.rq.teardown:
3022 bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, worker.process.pid, str(worker.process.returncode)))
3023 self.rq.finish_runqueue(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003024
3025 start = len(self.queue)
3026 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003027 self.queue = self.queue + (self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003028 except (OSError, IOError) as e:
3029 if e.errno != errno.EAGAIN:
3030 raise
3031 end = len(self.queue)
3032 found = True
Andrew Geissler595f6302022-01-24 19:11:47 +00003033 while found and self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003034 found = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003035 index = self.queue.find(b"</event>")
3036 while index != -1 and self.queue.startswith(b"<event>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003037 try:
3038 event = pickle.loads(self.queue[7:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003039 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
3040 if isinstance(e, pickle.UnpicklingError) and "truncated" in str(e):
3041 # The pickled data could contain "</event>" so search for the next occurance
3042 # unpickling again, this should be the only way an unpickle error could occur
3043 index = self.queue.find(b"</event>", index + 1)
3044 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003045 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
3046 bb.event.fire_from_worker(event, self.d)
Brad Bishop08902b02019-08-20 09:16:51 -04003047 if isinstance(event, taskUniHashUpdate):
3048 self.rqexec.updated_taskhash_queue.append((event.taskid, event.unihash))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003049 found = True
3050 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003051 index = self.queue.find(b"</event>")
3052 index = self.queue.find(b"</exitcode>")
3053 while index != -1 and self.queue.startswith(b"<exitcode>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003054 try:
3055 task, status = pickle.loads(self.queue[10:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05003056 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003057 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
Andrew Geissler95ac1b82021-03-31 14:34:31 -05003058 (_, _, _, taskfn) = split_tid_mcfn(task)
3059 fakerootlog = None
3060 if self.fakerootlogs and taskfn and taskfn in self.fakerootlogs:
3061 fakerootlog = self.fakerootlogs[taskfn]
3062 self.rqexec.runqueue_process_waitpid(task, status, fakerootlog=fakerootlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003063 found = True
3064 self.queue = self.queue[index+11:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003065 index = self.queue.find(b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003066 return (end > start)
3067
3068 def close(self):
3069 while self.read():
3070 continue
Andrew Geissler595f6302022-01-24 19:11:47 +00003071 if self.queue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003072 print("Warning, worker left partial message: %s" % self.queue)
3073 self.input.close()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003074
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003075def get_setscene_enforce_ignore_tasks(d, targets):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003076 if d.getVar('BB_SETSCENE_ENFORCE') != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003077 return None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003078 ignore_tasks = (d.getVar("BB_SETSCENE_ENFORCE_IGNORE_TASKS") or "").split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003079 outlist = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003080 for item in ignore_tasks[:]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003081 if item.startswith('%:'):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003082 for (mc, target, task, fn) in targets:
3083 outlist.append(target + ':' + item.split(':')[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003084 else:
3085 outlist.append(item)
3086 return outlist
3087
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003088def check_setscene_enforce_ignore_tasks(pn, taskname, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003089 import fnmatch
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003090 if ignore_tasks is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003091 item = '%s:%s' % (pn, taskname)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00003092 for ignore_tasks in ignore_tasks:
3093 if fnmatch.fnmatch(item, ignore_tasks):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003094 return True
3095 return False
3096 return True