blob: 54ef245a633960bc4b59f19b24c4e85a3e74bf1a [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 """
88 def __init__(self, total):
89 self.completed = 0
90 self.skipped = 0
91 self.failed = 0
92 self.active = 0
93 self.total = total
94
95 def copy(self):
96 obj = self.__class__(self.total)
97 obj.__dict__.update(self.__dict__)
98 return obj
99
100 def taskFailed(self):
101 self.active = self.active - 1
102 self.failed = self.failed + 1
103
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800104 def taskCompleted(self):
105 self.active = self.active - 1
106 self.completed = self.completed + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500107
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800108 def taskSkipped(self):
109 self.active = self.active + 1
110 self.skipped = self.skipped + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111
112 def taskActive(self):
113 self.active = self.active + 1
114
115# These values indicate the next step due to be run in the
116# runQueue state machine
117runQueuePrepare = 2
118runQueueSceneInit = 3
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500119runQueueRunning = 6
120runQueueFailed = 7
121runQueueCleanUp = 8
122runQueueComplete = 9
123
124class RunQueueScheduler(object):
125 """
126 Control the order tasks are scheduled in.
127 """
128 name = "basic"
129
130 def __init__(self, runqueue, rqdata):
131 """
132 The default scheduler just returns the first buildable task (the
133 priority map is sorted by task number)
134 """
135 self.rq = runqueue
136 self.rqdata = rqdata
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600137 self.numTasks = len(self.rqdata.runtaskentries)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500138
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600139 self.prio_map = [self.rqdata.runtaskentries.keys()]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500140
Brad Bishop08902b02019-08-20 09:16:51 -0400141 self.buildable = set()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800142 self.skip_maxthread = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500143 self.stamps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600144 for tid in self.rqdata.runtaskentries:
145 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
146 self.stamps[tid] = bb.build.stampfile(taskname, self.rqdata.dataCaches[mc], taskfn, noextra=True)
147 if tid in self.rq.runq_buildable:
148 self.buildable.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500149
150 self.rev_prio_map = None
151
152 def next_buildable_task(self):
153 """
154 Return the id of the first task we find that is buildable
155 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500156 # Once tasks are running we don't need to worry about them again
157 self.buildable.difference_update(self.rq.runq_running)
Brad Bishop08902b02019-08-20 09:16:51 -0400158 buildable = set(self.buildable)
Brad Bishop08902b02019-08-20 09:16:51 -0400159 buildable.difference_update(self.rq.holdoff_tasks)
160 buildable.intersection_update(self.rq.tasks_covered | self.rq.tasks_notcovered)
Brad Bishop96ff1982019-08-19 13:50:42 -0400161 if not buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500162 return None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800163
164 # Filter out tasks that have a max number of threads that have been exceeded
165 skip_buildable = {}
166 for running in self.rq.runq_running.difference(self.rq.runq_complete):
167 rtaskname = taskname_from_tid(running)
168 if rtaskname not in self.skip_maxthread:
169 self.skip_maxthread[rtaskname] = self.rq.cfgData.getVarFlag(rtaskname, "number_threads")
170 if not self.skip_maxthread[rtaskname]:
171 continue
172 if rtaskname in skip_buildable:
173 skip_buildable[rtaskname] += 1
174 else:
175 skip_buildable[rtaskname] = 1
176
Brad Bishop96ff1982019-08-19 13:50:42 -0400177 if len(buildable) == 1:
Brad Bishop08902b02019-08-20 09:16:51 -0400178 tid = buildable.pop()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800179 taskname = taskname_from_tid(tid)
180 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
181 return None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600182 stamp = self.stamps[tid]
183 if stamp not in self.rq.build_stamps.values():
184 return tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500185
186 if not self.rev_prio_map:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600187 self.rev_prio_map = {}
188 for tid in self.rqdata.runtaskentries:
189 self.rev_prio_map[tid] = self.prio_map.index(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500190
191 best = None
192 bestprio = None
Brad Bishop96ff1982019-08-19 13:50:42 -0400193 for tid in buildable:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800194 taskname = taskname_from_tid(tid)
195 if taskname in skip_buildable and skip_buildable[taskname] >= int(self.skip_maxthread[taskname]):
196 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600197 prio = self.rev_prio_map[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500198 if bestprio is None or bestprio > prio:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600199 stamp = self.stamps[tid]
200 if stamp in self.rq.build_stamps.values():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500201 continue
202 bestprio = prio
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600203 best = tid
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500204
205 return best
206
207 def next(self):
208 """
209 Return the id of the task we should build next
210 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800211 if self.rq.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500212 return self.next_buildable_task()
213
Brad Bishop316dfdd2018-06-25 12:45:53 -0400214 def newbuildable(self, task):
Brad Bishop08902b02019-08-20 09:16:51 -0400215 self.buildable.add(task)
216
217 def removebuildable(self, task):
218 self.buildable.remove(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500219
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500220 def describe_task(self, taskid):
221 result = 'ID %s' % taskid
222 if self.rev_prio_map:
223 result = result + (' pri %d' % self.rev_prio_map[taskid])
224 return result
225
226 def dump_prio(self, comment):
227 bb.debug(3, '%s (most important first):\n%s' %
228 (comment,
229 '\n'.join(['%d. %s' % (index + 1, self.describe_task(taskid)) for
230 index, taskid in enumerate(self.prio_map)])))
231
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500232class RunQueueSchedulerSpeed(RunQueueScheduler):
233 """
234 A scheduler optimised for speed. The priority map is sorted by task weight,
235 heavier weighted tasks (tasks needed by the most other tasks) are run first.
236 """
237 name = "speed"
238
239 def __init__(self, runqueue, rqdata):
240 """
241 The priority map is sorted by task weight.
242 """
243 RunQueueScheduler.__init__(self, runqueue, rqdata)
244
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600245 weights = {}
246 for tid in self.rqdata.runtaskentries:
247 weight = self.rqdata.runtaskentries[tid].weight
248 if not weight in weights:
249 weights[weight] = []
250 weights[weight].append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500251
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600252 self.prio_map = []
253 for weight in sorted(weights):
254 for w in weights[weight]:
255 self.prio_map.append(w)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500256
257 self.prio_map.reverse()
258
259class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed):
260 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500261 A scheduler optimised to complete .bb files as quickly as possible. The
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262 priority map is sorted by task weight, but then reordered so once a given
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500263 .bb file starts to build, it's completed as quickly as possible by
264 running all tasks related to the same .bb file one after the after.
265 This works well where disk space is at a premium and classes like OE's
266 rm_work are in force.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500267 """
268 name = "completion"
269
270 def __init__(self, runqueue, rqdata):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500271 super(RunQueueSchedulerCompletion, self).__init__(runqueue, rqdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500272
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500273 # Extract list of tasks for each recipe, with tasks sorted
274 # ascending from "must run first" (typically do_fetch) to
275 # "runs last" (do_build). The speed scheduler prioritizes
276 # tasks that must run first before the ones that run later;
277 # this is what we depend on here.
278 task_lists = {}
279 for taskid in self.prio_map:
280 fn, taskname = taskid.rsplit(':', 1)
281 task_lists.setdefault(fn, []).append(taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500282
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500283 # Now unify the different task lists. The strategy is that
284 # common tasks get skipped and new ones get inserted after the
285 # preceeding common one(s) as they are found. Because task
286 # lists should differ only by their number of tasks, but not
287 # the ordering of the common tasks, this should result in a
288 # deterministic result that is a superset of the individual
289 # task ordering.
290 all_tasks = []
291 for recipe, new_tasks in task_lists.items():
292 index = 0
293 old_task = all_tasks[index] if index < len(all_tasks) else None
294 for new_task in new_tasks:
295 if old_task == new_task:
296 # Common task, skip it. This is the fast-path which
297 # avoids a full search.
298 index += 1
299 old_task = all_tasks[index] if index < len(all_tasks) else None
300 else:
301 try:
302 index = all_tasks.index(new_task)
303 # Already present, just not at the current
304 # place. We re-synchronized by changing the
305 # index so that it matches again. Now
306 # move on to the next existing task.
307 index += 1
308 old_task = all_tasks[index] if index < len(all_tasks) else None
309 except ValueError:
310 # Not present. Insert before old_task, which
311 # remains the same (but gets shifted back).
312 all_tasks.insert(index, new_task)
313 index += 1
314 bb.debug(3, 'merged task list: %s' % all_tasks)
315
316 # Now reverse the order so that tasks that finish the work on one
317 # recipe are considered more imporant (= come first). The ordering
318 # is now so that do_build is most important.
319 all_tasks.reverse()
320
321 # Group tasks of the same kind before tasks of less important
322 # kinds at the head of the queue (because earlier = lower
323 # priority number = runs earlier), while preserving the
324 # ordering by recipe. If recipe foo is more important than
325 # bar, then the goal is to work on foo's do_populate_sysroot
326 # before bar's do_populate_sysroot and on the more important
327 # tasks of foo before any of the less important tasks in any
328 # other recipe (if those other recipes are more important than
329 # foo).
330 #
331 # All of this only applies when tasks are runable. Explicit
332 # dependencies still override this ordering by priority.
333 #
334 # Here's an example why this priority re-ordering helps with
335 # minimizing disk usage. Consider a recipe foo with a higher
336 # priority than bar where foo DEPENDS on bar. Then the
337 # implicit rule (from base.bbclass) is that foo's do_configure
338 # depends on bar's do_populate_sysroot. This ensures that
339 # bar's do_populate_sysroot gets done first. Normally the
340 # tasks from foo would continue to run once that is done, and
341 # bar only gets completed and cleaned up later. By ordering
342 # bar's task that depend on bar's do_populate_sysroot before foo's
343 # do_configure, that problem gets avoided.
344 task_index = 0
345 self.dump_prio('original priorities')
346 for task in all_tasks:
347 for index in range(task_index, self.numTasks):
348 taskid = self.prio_map[index]
349 taskname = taskid.rsplit(':', 1)[1]
350 if taskname == task:
351 del self.prio_map[index]
352 self.prio_map.insert(task_index, taskid)
353 task_index += 1
354 self.dump_prio('completion priorities')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500355
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600356class RunTaskEntry(object):
357 def __init__(self):
358 self.depends = set()
359 self.revdeps = set()
360 self.hash = None
Brad Bishop19323692019-04-05 15:28:33 -0400361 self.unihash = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600362 self.task = None
363 self.weight = 1
364
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500365class RunQueueData:
366 """
367 BitBake Run Queue implementation
368 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600369 def __init__(self, rq, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500370 self.cooker = cooker
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600371 self.dataCaches = dataCaches
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500372 self.taskData = taskData
373 self.targets = targets
374 self.rq = rq
375 self.warn_multi_bb = False
376
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500377 self.stampwhitelist = cfgData.getVar("BB_STAMP_WHITELIST") or ""
378 self.multi_provider_whitelist = (cfgData.getVar("MULTI_PROVIDER_WHITELIST") or "").split()
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500379 self.setscenewhitelist = get_setscene_enforce_whitelist(cfgData, targets)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600380 self.setscenewhitelist_checked = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500381 self.setscene_enforce = (cfgData.getVar('BB_SETSCENE_ENFORCE') == "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600382 self.init_progress_reporter = bb.progress.DummyMultiStageProcessProgressReporter()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500383
384 self.reset()
385
386 def reset(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600387 self.runtaskentries = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500388
389 def runq_depends_names(self, ids):
390 import re
391 ret = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600392 for id in ids:
393 nam = os.path.basename(id)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500394 nam = re.sub("_[^,]*,", ",", nam)
395 ret.extend([nam])
396 return ret
397
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600398 def get_task_hash(self, tid):
399 return self.runtaskentries[tid].hash
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500400
Brad Bishop19323692019-04-05 15:28:33 -0400401 def get_task_unihash(self, tid):
402 return self.runtaskentries[tid].unihash
403
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600404 def get_user_idstring(self, tid, task_name_suffix = ""):
405 return tid + task_name_suffix
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500406
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500407 def get_short_user_idstring(self, task, task_name_suffix = ""):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500408 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
409 pn = self.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600410 taskname = taskname_from_tid(task) + task_name_suffix
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500411 return "%s:%s" % (pn, taskname)
412
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500413 def circular_depchains_handler(self, tasks):
414 """
415 Some tasks aren't buildable, likely due to circular dependency issues.
416 Identify the circular dependencies and print them in a user readable format.
417 """
418 from copy import deepcopy
419
420 valid_chains = []
421 explored_deps = {}
422 msgs = []
423
Andrew Geissler99467da2019-02-25 18:54:23 -0600424 class TooManyLoops(Exception):
425 pass
426
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500427 def chain_reorder(chain):
428 """
429 Reorder a dependency chain so the lowest task id is first
430 """
431 lowest = 0
432 new_chain = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600433 for entry in range(len(chain)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500434 if chain[entry] < chain[lowest]:
435 lowest = entry
436 new_chain.extend(chain[lowest:])
437 new_chain.extend(chain[:lowest])
438 return new_chain
439
440 def chain_compare_equal(chain1, chain2):
441 """
442 Compare two dependency chains and see if they're the same
443 """
444 if len(chain1) != len(chain2):
445 return False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600446 for index in range(len(chain1)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500447 if chain1[index] != chain2[index]:
448 return False
449 return True
450
451 def chain_array_contains(chain, chain_array):
452 """
453 Return True if chain_array contains chain
454 """
455 for ch in chain_array:
456 if chain_compare_equal(ch, chain):
457 return True
458 return False
459
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600460 def find_chains(tid, prev_chain):
461 prev_chain.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500462 total_deps = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600463 total_deps.extend(self.runtaskentries[tid].revdeps)
464 for revdep in self.runtaskentries[tid].revdeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500465 if revdep in prev_chain:
466 idx = prev_chain.index(revdep)
467 # To prevent duplicates, reorder the chain to start with the lowest taskid
468 # and search through an array of those we've already printed
469 chain = prev_chain[idx:]
470 new_chain = chain_reorder(chain)
471 if not chain_array_contains(new_chain, valid_chains):
472 valid_chains.append(new_chain)
473 msgs.append("Dependency loop #%d found:\n" % len(valid_chains))
474 for dep in new_chain:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600475 msgs.append(" Task %s (dependent Tasks %s)\n" % (dep, self.runq_depends_names(self.runtaskentries[dep].depends)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500476 msgs.append("\n")
477 if len(valid_chains) > 10:
478 msgs.append("Aborted dependency loops search after 10 matches.\n")
Andrew Geissler99467da2019-02-25 18:54:23 -0600479 raise TooManyLoops
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500480 continue
481 scan = False
482 if revdep not in explored_deps:
483 scan = True
484 elif revdep in explored_deps[revdep]:
485 scan = True
486 else:
487 for dep in prev_chain:
488 if dep in explored_deps[revdep]:
489 scan = True
490 if scan:
491 find_chains(revdep, copy.deepcopy(prev_chain))
492 for dep in explored_deps[revdep]:
493 if dep not in total_deps:
494 total_deps.append(dep)
495
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600496 explored_deps[tid] = total_deps
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500497
Andrew Geissler99467da2019-02-25 18:54:23 -0600498 try:
499 for task in tasks:
500 find_chains(task, [])
501 except TooManyLoops:
502 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500503
504 return msgs
505
506 def calculate_task_weights(self, endpoints):
507 """
508 Calculate a number representing the "weight" of each task. Heavier weighted tasks
509 have more dependencies and hence should be executed sooner for maximum speed.
510
511 This function also sanity checks the task list finding tasks that are not
512 possible to execute due to circular dependencies.
513 """
514
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600515 numTasks = len(self.runtaskentries)
516 weight = {}
517 deps_left = {}
518 task_done = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500519
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600520 for tid in self.runtaskentries:
521 task_done[tid] = False
522 weight[tid] = 1
523 deps_left[tid] = len(self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500524
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600525 for tid in endpoints:
526 weight[tid] = 10
527 task_done[tid] = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500528
529 while True:
530 next_points = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600531 for tid in endpoints:
532 for revdep in self.runtaskentries[tid].depends:
533 weight[revdep] = weight[revdep] + weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534 deps_left[revdep] = deps_left[revdep] - 1
535 if deps_left[revdep] == 0:
536 next_points.append(revdep)
537 task_done[revdep] = True
538 endpoints = next_points
539 if len(next_points) == 0:
540 break
541
542 # Circular dependency sanity check
543 problem_tasks = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600544 for tid in self.runtaskentries:
545 if task_done[tid] is False or deps_left[tid] != 0:
546 problem_tasks.append(tid)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600547 logger.debug2("Task %s is not buildable", tid)
548 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 -0600549 self.runtaskentries[tid].weight = weight[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500550
551 if problem_tasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600552 message = "%s unbuildable tasks were found.\n" % len(problem_tasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500553 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"
554 message = message + "Identifying dependency loops (this may take a short while)...\n"
555 logger.error(message)
556
557 msgs = self.circular_depchains_handler(problem_tasks)
558
559 message = "\n"
560 for msg in msgs:
561 message = message + msg
562 bb.msg.fatal("RunQueue", message)
563
564 return weight
565
566 def prepare(self):
567 """
568 Turn a set of taskData into a RunQueue and compute data needed
569 to optimise the execution order.
570 """
571
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600572 runq_build = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573 recursivetasks = {}
574 recursiveitasks = {}
575 recursivetasksselfref = set()
576
577 taskData = self.taskData
578
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600579 found = False
580 for mc in self.taskData:
581 if len(taskData[mc].taskentries) > 0:
582 found = True
583 break
584 if not found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500585 # Nothing to do
586 return 0
587
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600588 self.init_progress_reporter.start()
589 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500590
591 # Step A - Work out a list of tasks to run
592 #
593 # Taskdata gives us a list of possible providers for every build and run
594 # target ordered by priority. It also gives information on each of those
595 # providers.
596 #
597 # To create the actual list of tasks to execute we fix the list of
598 # providers and then resolve the dependencies into task IDs. This
599 # process is repeated for each type of dependency (tdepends, deptask,
600 # rdeptast, recrdeptask, idepends).
601
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600602 def add_build_dependencies(depids, tasknames, depends, mc):
603 for depname in depids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500604 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600605 if depname not in taskData[mc].build_targets or not taskData[mc].build_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500606 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600607 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500608 if depdata is None:
609 continue
610 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600611 t = depdata + ":" + taskname
612 if t in taskData[mc].taskentries:
613 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500614
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600615 def add_runtime_dependencies(depids, tasknames, depends, mc):
616 for depname in depids:
617 if depname not in taskData[mc].run_targets or not taskData[mc].run_targets[depname]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500618 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600619 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500620 if depdata is None:
621 continue
622 for taskname in tasknames:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600623 t = depdata + ":" + taskname
624 if t in taskData[mc].taskentries:
625 depends.add(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500626
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800627 def add_mc_dependencies(mc, tid):
628 mcdeps = taskData[mc].get_mcdepends()
629 for dep in mcdeps:
630 mcdependency = dep.split(':')
631 pn = mcdependency[3]
632 frommc = mcdependency[1]
633 mcdep = mcdependency[2]
634 deptask = mcdependency[4]
635 if mc == frommc:
636 fn = taskData[mcdep].build_targets[pn][0]
637 newdep = '%s:%s' % (fn,deptask)
638 taskData[mc].taskentries[tid].tdepends.append(newdep)
639
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600640 for mc in taskData:
641 for tid in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500642
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600643 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
644 #runtid = build_tid(mc, fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500645
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600646 #logger.debug2("Processing %s,%s:%s", mc, fn, taskname)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600647
648 depends = set()
649 task_deps = self.dataCaches[mc].task_deps[taskfn]
650
651 self.runtaskentries[tid] = RunTaskEntry()
652
653 if fn in taskData[mc].failed_fns:
654 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500655
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800656 # We add multiconfig dependencies before processing internal task deps (tdepends)
657 if 'mcdepends' in task_deps and taskname in task_deps['mcdepends']:
658 add_mc_dependencies(mc, tid)
659
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500660 # Resolve task internal dependencies
661 #
662 # e.g. addtask before X after Y
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600663 for t in taskData[mc].taskentries[tid].tdepends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800664 (depmc, depfn, deptaskname, _) = split_tid_mcfn(t)
665 depends.add(build_tid(depmc, depfn, deptaskname))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500666
667 # Resolve 'deptask' dependencies
668 #
669 # e.g. do_sometask[deptask] = "do_someothertask"
670 # (makes sure sometask runs after someothertask of all DEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600671 if 'deptask' in task_deps and taskname in task_deps['deptask']:
672 tasknames = task_deps['deptask'][taskname].split()
673 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500674
675 # Resolve 'rdeptask' dependencies
676 #
677 # e.g. do_sometask[rdeptask] = "do_someothertask"
678 # (makes sure sometask runs after someothertask of all RDEPENDS)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600679 if 'rdeptask' in task_deps and taskname in task_deps['rdeptask']:
680 tasknames = task_deps['rdeptask'][taskname].split()
681 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500682
683 # Resolve inter-task dependencies
684 #
685 # e.g. do_sometask[depends] = "targetname:do_someothertask"
686 # (makes sure sometask runs after targetname's someothertask)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600687 idepends = taskData[mc].taskentries[tid].idepends
688 for (depname, idependtask) in idepends:
689 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 -0500690 # Won't be in build_targets if ASSUME_PROVIDED
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600691 depdata = taskData[mc].build_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500692 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600693 t = depdata + ":" + idependtask
694 depends.add(t)
695 if t not in taskData[mc].taskentries:
696 bb.msg.fatal("RunQueue", "Task %s in %s depends upon non-existent task %s in %s" % (taskname, fn, idependtask, depdata))
697 irdepends = taskData[mc].taskentries[tid].irdepends
698 for (depname, idependtask) in irdepends:
699 if depname in taskData[mc].run_targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500700 # Won't be in run_targets if ASSUME_PROVIDED
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500701 if not taskData[mc].run_targets[depname]:
702 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600703 depdata = taskData[mc].run_targets[depname][0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500704 if depdata is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600705 t = depdata + ":" + idependtask
706 depends.add(t)
707 if t not in taskData[mc].taskentries:
708 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 -0500709
710 # Resolve recursive 'recrdeptask' dependencies (Part A)
711 #
712 # e.g. do_sometask[recrdeptask] = "do_someothertask"
713 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
714 # We cover the recursive part of the dependencies below
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600715 if 'recrdeptask' in task_deps and taskname in task_deps['recrdeptask']:
716 tasknames = task_deps['recrdeptask'][taskname].split()
717 recursivetasks[tid] = tasknames
718 add_build_dependencies(taskData[mc].depids[taskfn], tasknames, depends, mc)
719 add_runtime_dependencies(taskData[mc].rdepids[taskfn], tasknames, depends, mc)
720 if taskname in tasknames:
721 recursivetasksselfref.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500722
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 if 'recideptask' in task_deps and taskname in task_deps['recideptask']:
724 recursiveitasks[tid] = []
725 for t in task_deps['recideptask'][taskname].split():
726 newdep = build_tid(mc, fn, t)
727 recursiveitasks[tid].append(newdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500728
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600729 self.runtaskentries[tid].depends = depends
Brad Bishop316dfdd2018-06-25 12:45:53 -0400730 # Remove all self references
731 self.runtaskentries[tid].depends.discard(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500732
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600733 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500734
Brad Bishop316dfdd2018-06-25 12:45:53 -0400735 self.init_progress_reporter.next_stage()
736
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500737 # Resolve recursive 'recrdeptask' dependencies (Part B)
738 #
739 # e.g. do_sometask[recrdeptask] = "do_someothertask"
740 # (makes sure sometask runs after someothertask of all DEPENDS, RDEPENDS and intertask dependencies, recursively)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600741 # 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 -0600742
Brad Bishop316dfdd2018-06-25 12:45:53 -0400743 # Generating/interating recursive lists of dependencies is painful and potentially slow
744 # Precompute recursive task dependencies here by:
745 # a) create a temp list of reverse dependencies (revdeps)
746 # b) walk up the ends of the chains (when a given task no longer has dependencies i.e. len(deps) == 0)
747 # c) combine the total list of dependencies in cumulativedeps
748 # d) optimise by pre-truncating 'task' off the items in cumulativedeps (keeps items in sets lower)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500750
Brad Bishop316dfdd2018-06-25 12:45:53 -0400751 revdeps = {}
752 deps = {}
753 cumulativedeps = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600754 for tid in self.runtaskentries:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400755 deps[tid] = set(self.runtaskentries[tid].depends)
756 revdeps[tid] = set()
757 cumulativedeps[tid] = set()
758 # Generate a temp list of reverse dependencies
759 for tid in self.runtaskentries:
760 for dep in self.runtaskentries[tid].depends:
761 revdeps[dep].add(tid)
762 # Find the dependency chain endpoints
763 endpoints = set()
764 for tid in self.runtaskentries:
765 if len(deps[tid]) == 0:
766 endpoints.add(tid)
767 # Iterate the chains collating dependencies
768 while endpoints:
769 next = set()
770 for tid in endpoints:
771 for dep in revdeps[tid]:
772 cumulativedeps[dep].add(fn_from_tid(tid))
773 cumulativedeps[dep].update(cumulativedeps[tid])
774 if tid in deps[dep]:
775 deps[dep].remove(tid)
776 if len(deps[dep]) == 0:
777 next.add(dep)
778 endpoints = next
779 #for tid in deps:
780 # if len(deps[tid]) != 0:
781 # bb.warn("Sanity test failure, dependencies left for %s (%s)" % (tid, deps[tid]))
782
783 # Loop here since recrdeptasks can depend upon other recrdeptasks and we have to
784 # resolve these recursively until we aren't adding any further extra dependencies
785 extradeps = True
786 while extradeps:
787 extradeps = 0
788 for tid in recursivetasks:
789 tasknames = recursivetasks[tid]
790
791 totaldeps = set(self.runtaskentries[tid].depends)
792 if tid in recursiveitasks:
793 totaldeps.update(recursiveitasks[tid])
794 for dep in recursiveitasks[tid]:
795 if dep not in self.runtaskentries:
796 continue
797 totaldeps.update(self.runtaskentries[dep].depends)
798
799 deps = set()
800 for dep in totaldeps:
801 if dep in cumulativedeps:
802 deps.update(cumulativedeps[dep])
803
804 for t in deps:
805 for taskname in tasknames:
806 newtid = t + ":" + taskname
807 if newtid == tid:
808 continue
809 if newtid in self.runtaskentries and newtid not in self.runtaskentries[tid].depends:
810 extradeps += 1
811 self.runtaskentries[tid].depends.add(newtid)
812
813 # Handle recursive tasks which depend upon other recursive tasks
814 deps = set()
815 for dep in self.runtaskentries[tid].depends.intersection(recursivetasks):
816 deps.update(self.runtaskentries[dep].depends.difference(self.runtaskentries[tid].depends))
817 for newtid in deps:
818 for taskname in tasknames:
819 if not newtid.endswith(":" + taskname):
820 continue
821 if newtid in self.runtaskentries:
822 extradeps += 1
823 self.runtaskentries[tid].depends.add(newtid)
824
825 bb.debug(1, "Added %s recursive dependencies in this loop" % extradeps)
826
827 # Remove recrdeptask circular references so that do_a[recrdeptask] = "do_a do_b" can work
828 for tid in recursivetasksselfref:
829 self.runtaskentries[tid].depends.difference_update(recursivetasksselfref)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600830
831 self.init_progress_reporter.next_stage()
832
833 #self.dump_data()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834
835 # Step B - Mark all active tasks
836 #
837 # Start with the tasks we were asked to run and mark all dependencies
838 # as active too. If the task is to be 'forced', clear its stamp. Once
839 # all active tasks are marked, prune the ones we don't need.
840
841 logger.verbose("Marking Active Tasks")
842
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600843 def mark_active(tid, depth):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844 """
845 Mark an item as active along with its depends
846 (calls itself recursively)
847 """
848
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600849 if tid in runq_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500850 return
851
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600852 runq_build[tid] = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500853
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600854 depends = self.runtaskentries[tid].depends
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500855 for depend in depends:
856 mark_active(depend, depth+1)
857
Brad Bishop79641f22019-09-10 07:20:22 -0400858 def invalidate_task(tid, error_nostamp):
859 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
860 taskdep = self.dataCaches[mc].task_deps[taskfn]
861 if fn + ":" + taskname not in taskData[mc].taskentries:
862 logger.warning("Task %s does not exist, invalidating this task will have no effect" % taskname)
863 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
864 if error_nostamp:
865 bb.fatal("Task %s is marked nostamp, cannot invalidate this task" % taskname)
866 else:
867 bb.debug(1, "Task %s is marked nostamp, cannot invalidate this task" % taskname)
868 else:
869 logger.verbose("Invalidate task %s, %s", taskname, fn)
870 bb.parse.siggen.invalidate_task(taskname, self.dataCaches[mc], taskfn)
871
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600872 self.target_tids = []
873 for (mc, target, task, fn) in self.targets:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500874
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600875 if target not in taskData[mc].build_targets or not taskData[mc].build_targets[target]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500876 continue
877
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600878 if target in taskData[mc].failed_deps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500879 continue
880
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500881 parents = False
882 if task.endswith('-'):
883 parents = True
884 task = task[:-1]
885
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600886 if fn in taskData[mc].failed_fns:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500887 continue
888
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600889 # fn already has mc prefix
890 tid = fn + ":" + task
891 self.target_tids.append(tid)
892 if tid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500893 import difflib
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600894 tasks = []
895 for x in taskData[mc].taskentries:
896 if x.startswith(fn + ":"):
897 tasks.append(taskname_from_tid(x))
898 close_matches = difflib.get_close_matches(task, tasks, cutoff=0.7)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500899 if close_matches:
900 extra = ". Close matches:\n %s" % "\n ".join(close_matches)
901 else:
902 extra = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600903 bb.msg.fatal("RunQueue", "Task %s does not exist for target %s (%s)%s" % (task, target, tid, extra))
904
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500905 # For tasks called "XXXX-", ony run their dependencies
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500906 if parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600907 for i in self.runtaskentries[tid].depends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500908 mark_active(i, 1)
909 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600910 mark_active(tid, 1)
911
912 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500913
914 # Step C - Prune all inactive tasks
915 #
916 # Once all active tasks are marked, prune the ones we don't need.
917
Brad Bishop316dfdd2018-06-25 12:45:53 -0400918 delcount = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600919 for tid in list(self.runtaskentries.keys()):
920 if tid not in runq_build:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400921 delcount[tid] = self.runtaskentries[tid]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600922 del self.runtaskentries[tid]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600923
Brad Bishop316dfdd2018-06-25 12:45:53 -0400924 # Handle --runall
925 if self.cooker.configuration.runall:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500926 # re-run the mark_active and then drop unused tasks from new list
927 runq_build = {}
Brad Bishop316dfdd2018-06-25 12:45:53 -0400928
929 for task in self.cooker.configuration.runall:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500930 if not task.startswith("do_"):
931 task = "do_{0}".format(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400932 runall_tids = set()
933 for tid in list(self.runtaskentries):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500934 wanttid = "{0}:{1}".format(fn_from_tid(tid), task)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400935 if wanttid in delcount:
936 self.runtaskentries[wanttid] = delcount[wanttid]
937 if wanttid in self.runtaskentries:
938 runall_tids.add(wanttid)
939
940 for tid in list(runall_tids):
941 mark_active(tid,1)
Brad Bishop79641f22019-09-10 07:20:22 -0400942 if self.cooker.configuration.force:
943 invalidate_task(tid, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500944
945 for tid in list(self.runtaskentries.keys()):
946 if tid not in runq_build:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400947 delcount[tid] = self.runtaskentries[tid]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500948 del self.runtaskentries[tid]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500949
950 if len(self.runtaskentries) == 0:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400951 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)))
952
953 self.init_progress_reporter.next_stage()
954
955 # Handle runonly
956 if self.cooker.configuration.runonly:
957 # re-run the mark_active and then drop unused tasks from new list
958 runq_build = {}
959
960 for task in self.cooker.configuration.runonly:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500961 if not task.startswith("do_"):
962 task = "do_{0}".format(task)
963 runonly_tids = { k: v for k, v in self.runtaskentries.items() if taskname_from_tid(k) == task }
Brad Bishop316dfdd2018-06-25 12:45:53 -0400964
965 for tid in list(runonly_tids):
966 mark_active(tid,1)
Brad Bishop79641f22019-09-10 07:20:22 -0400967 if self.cooker.configuration.force:
968 invalidate_task(tid, False)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400969
970 for tid in list(self.runtaskentries.keys()):
971 if tid not in runq_build:
972 delcount[tid] = self.runtaskentries[tid]
973 del self.runtaskentries[tid]
974
975 if len(self.runtaskentries) == 0:
976 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 -0500977
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500978 #
979 # Step D - Sanity checks and computation
980 #
981
982 # Check to make sure we still have tasks to run
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600983 if len(self.runtaskentries) == 0:
984 if not taskData[''].abort:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500985 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.")
986 else:
987 bb.msg.fatal("RunQueue", "No active tasks and not in --continue mode?! Please report this bug.")
988
Brad Bishop316dfdd2018-06-25 12:45:53 -0400989 logger.verbose("Pruned %s inactive tasks, %s left", len(delcount), len(self.runtaskentries))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500990
991 logger.verbose("Assign Weightings")
992
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600993 self.init_progress_reporter.next_stage()
994
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500995 # Generate a list of reverse dependencies to ease future calculations
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600996 for tid in self.runtaskentries:
997 for dep in self.runtaskentries[tid].depends:
998 self.runtaskentries[dep].revdeps.add(tid)
999
1000 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001001
1002 # Identify tasks at the end of dependency chains
1003 # Error on circular dependency loops (length two)
1004 endpoints = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001005 for tid in self.runtaskentries:
1006 revdeps = self.runtaskentries[tid].revdeps
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001007 if len(revdeps) == 0:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001008 endpoints.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001009 for dep in revdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001010 if dep in self.runtaskentries[tid].depends:
1011 bb.msg.fatal("RunQueue", "Task %s has circular dependency on %s" % (tid, dep))
1012
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001013
1014 logger.verbose("Compute totals (have %s endpoint(s))", len(endpoints))
1015
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001016 self.init_progress_reporter.next_stage()
1017
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001018 # Calculate task weights
1019 # Check of higher length circular dependencies
1020 self.runq_weight = self.calculate_task_weights(endpoints)
1021
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001022 self.init_progress_reporter.next_stage()
1023
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001024 # Sanity Check - Check for multiple tasks building the same provider
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001025 for mc in self.dataCaches:
1026 prov_list = {}
1027 seen_fn = []
1028 for tid in self.runtaskentries:
1029 (tidmc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1030 if taskfn in seen_fn:
1031 continue
1032 if mc != tidmc:
1033 continue
1034 seen_fn.append(taskfn)
1035 for prov in self.dataCaches[mc].fn_provides[taskfn]:
1036 if prov not in prov_list:
1037 prov_list[prov] = [taskfn]
1038 elif taskfn not in prov_list[prov]:
1039 prov_list[prov].append(taskfn)
1040 for prov in prov_list:
1041 if len(prov_list[prov]) < 2:
1042 continue
1043 if prov in self.multi_provider_whitelist:
1044 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001045 seen_pn = []
1046 # If two versions of the same PN are being built its fatal, we don't support it.
1047 for fn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001048 pn = self.dataCaches[mc].pkg_fn[fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001049 if pn not in seen_pn:
1050 seen_pn.append(pn)
1051 else:
1052 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))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001053 msg = "Multiple .bb files are due to be built which each provide %s:\n %s" % (prov, "\n ".join(prov_list[prov]))
1054 #
1055 # Construct a list of things which uniquely depend on each provider
1056 # since this may help the user figure out which dependency is triggering this warning
1057 #
1058 msg += "\nA list of tasks depending on these providers is shown and may help explain where the dependency comes from."
1059 deplist = {}
1060 commondeps = None
1061 for provfn in prov_list[prov]:
1062 deps = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001063 for tid in self.runtaskentries:
1064 fn = fn_from_tid(tid)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001065 if fn != provfn:
1066 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001067 for dep in self.runtaskentries[tid].revdeps:
1068 fn = fn_from_tid(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001069 if fn == provfn:
1070 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001071 deps.add(dep)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001072 if not commondeps:
1073 commondeps = set(deps)
1074 else:
1075 commondeps &= deps
1076 deplist[provfn] = deps
1077 for provfn in deplist:
1078 msg += "\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps))
1079 #
1080 # Construct a list of provides and runtime providers for each recipe
1081 # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC)
1082 #
1083 msg += "\nIt could be that one recipe provides something the other doesn't and should. The following provider and runtime provider differences may be helpful."
1084 provide_results = {}
1085 rprovide_results = {}
1086 commonprovs = None
1087 commonrprovs = None
1088 for provfn in prov_list[prov]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001089 provides = set(self.dataCaches[mc].fn_provides[provfn])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001090 rprovides = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001091 for rprovide in self.dataCaches[mc].rproviders:
1092 if provfn in self.dataCaches[mc].rproviders[rprovide]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001093 rprovides.add(rprovide)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001094 for package in self.dataCaches[mc].packages:
1095 if provfn in self.dataCaches[mc].packages[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001096 rprovides.add(package)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001097 for package in self.dataCaches[mc].packages_dynamic:
1098 if provfn in self.dataCaches[mc].packages_dynamic[package]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001099 rprovides.add(package)
1100 if not commonprovs:
1101 commonprovs = set(provides)
1102 else:
1103 commonprovs &= provides
1104 provide_results[provfn] = provides
1105 if not commonrprovs:
1106 commonrprovs = set(rprovides)
1107 else:
1108 commonrprovs &= rprovides
1109 rprovide_results[provfn] = rprovides
1110 #msg += "\nCommon provides:\n %s" % ("\n ".join(commonprovs))
1111 #msg += "\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs))
1112 for provfn in prov_list[prov]:
1113 msg += "\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs))
1114 msg += "\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs))
1115
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001116 if self.warn_multi_bb:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001117 logger.verbnote(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001118 else:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001119 logger.error(msg)
1120
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001121 self.init_progress_reporter.next_stage()
1122
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001123 # Create a whitelist usable by the stamp checks
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001124 self.stampfnwhitelist = {}
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001125 for mc in self.taskData:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001126 self.stampfnwhitelist[mc] = []
1127 for entry in self.stampwhitelist.split():
1128 if entry not in self.taskData[mc].build_targets:
1129 continue
1130 fn = self.taskData.build_targets[entry][0]
1131 self.stampfnwhitelist[mc].append(fn)
1132
1133 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001134
1135 # Iterate over the task list looking for tasks with a 'setscene' function
Andrew Geissler82c905d2020-04-13 13:39:40 -05001136 self.runq_setscene_tids = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001137 if not self.cooker.configuration.nosetscene:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001138 for tid in self.runtaskentries:
1139 (mc, fn, taskname, _) = split_tid_mcfn(tid)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001140 setscenetid = tid + "_setscene"
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001141 if setscenetid not in taskData[mc].taskentries:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001142 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -05001143 self.runq_setscene_tids.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001144
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001145 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001146
1147 # Invalidate task if force mode active
1148 if self.cooker.configuration.force:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001149 for tid in self.target_tids:
1150 invalidate_task(tid, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001151
1152 # Invalidate task if invalidate mode active
1153 if self.cooker.configuration.invalidate_stamp:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001154 for tid in self.target_tids:
1155 fn = fn_from_tid(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001156 for st in self.cooker.configuration.invalidate_stamp.split(','):
1157 if not st.startswith("do_"):
1158 st = "do_%s" % st
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001159 invalidate_task(fn + ":" + st, True)
1160
1161 self.init_progress_reporter.next_stage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001162
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001163 # Create and print to the logs a virtual/xxxx -> PN (fn) table
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001164 for mc in taskData:
1165 virtmap = taskData[mc].get_providermap(prefix="virtual/")
1166 virtpnmap = {}
1167 for v in virtmap:
1168 virtpnmap[v] = self.dataCaches[mc].pkg_fn[virtmap[v]]
1169 bb.debug(2, "%s resolved to: %s (%s)" % (v, virtpnmap[v], virtmap[v]))
1170 if hasattr(bb.parse.siggen, "tasks_resolved"):
1171 bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCaches[mc])
1172
1173 self.init_progress_reporter.next_stage()
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001174
Brad Bishop00e122a2019-10-05 11:10:57 -04001175 bb.parse.siggen.set_setscene_tasks(self.runq_setscene_tids)
1176
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001177 # Iterate over the task list and call into the siggen code
1178 dealtwith = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001179 todeal = set(self.runtaskentries)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001180 while len(todeal) > 0:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001181 for tid in todeal.copy():
1182 if len(self.runtaskentries[tid].depends - dealtwith) == 0:
1183 dealtwith.add(tid)
1184 todeal.remove(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001185 self.prepare_task_hash(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001186
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001187 bb.parse.siggen.writeout_file_checksum_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001188
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001189 #self.dump_data()
1190 return len(self.runtaskentries)
1191
Brad Bishop19323692019-04-05 15:28:33 -04001192 def prepare_task_hash(self, tid):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001193 dc = bb.parse.siggen.get_data_caches(self.dataCaches, mc_from_tid(tid))
1194 bb.parse.siggen.prep_taskhash(tid, self.runtaskentries[tid].depends, dc)
1195 self.runtaskentries[tid].hash = bb.parse.siggen.get_taskhash(tid, self.runtaskentries[tid].depends, dc)
Brad Bishop08902b02019-08-20 09:16:51 -04001196 self.runtaskentries[tid].unihash = bb.parse.siggen.get_unihash(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001197
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001198 def dump_data(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001199 """
1200 Dump some debug information on the internal data structures
1201 """
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001202 logger.debug3("run_tasks:")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001203 for tid in self.runtaskentries:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001204 logger.debug3(" %s: %s Deps %s RevDeps %s", tid,
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001205 self.runtaskentries[tid].weight,
1206 self.runtaskentries[tid].depends,
1207 self.runtaskentries[tid].revdeps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001208
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001209class RunQueueWorker():
1210 def __init__(self, process, pipe):
1211 self.process = process
1212 self.pipe = pipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001213
1214class RunQueue:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001215 def __init__(self, cooker, cfgData, dataCaches, taskData, targets):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001216
1217 self.cooker = cooker
1218 self.cfgData = cfgData
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001219 self.rqdata = RunQueueData(self, cooker, cfgData, dataCaches, taskData, targets)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001220
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001221 self.stamppolicy = cfgData.getVar("BB_STAMP_POLICY") or "perfile"
1222 self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION") or None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001223 self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID") or None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001224
1225 self.state = runQueuePrepare
1226
1227 # For disk space monitor
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001228 # Invoked at regular time intervals via the bitbake heartbeat event
1229 # while the build is running. We generate a unique name for the handler
1230 # here, just in case that there ever is more than one RunQueue instance,
Brad Bishop96ff1982019-08-19 13:50:42 -04001231 # start the handler when reaching runQueueSceneInit, and stop it when
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001232 # done with the build.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001233 self.dm = monitordisk.diskMonitor(cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001234 self.dm_event_handler_name = '_bb_diskmonitor_' + str(id(self))
1235 self.dm_event_handler_registered = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001236 self.rqexe = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001237 self.worker = {}
1238 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001239
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001240 def _start_worker(self, mc, fakeroot = False, rqexec = None):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001241 logger.debug("Starting bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001242 magic = "decafbad"
1243 if self.cooker.configuration.profile:
1244 magic = "decafbadbad"
1245 if fakeroot:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001246 magic = magic + "beef"
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001247 mcdata = self.cooker.databuilder.mcdata[mc]
Brad Bishop19323692019-04-05 15:28:33 -04001248 fakerootcmd = shlex.split(mcdata.getVar("FAKEROOTCMD"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001249 fakerootenv = (mcdata.getVar("FAKEROOTBASEENV") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001250 env = os.environ.copy()
1251 for key, value in (var.split('=') for var in fakerootenv):
1252 env[key] = value
Brad Bishop19323692019-04-05 15:28:33 -04001253 worker = subprocess.Popen(fakerootcmd + ["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE, env=env)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001254 else:
1255 worker = subprocess.Popen(["bitbake-worker", magic], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
1256 bb.utils.nonblockingfd(worker.stdout)
1257 workerpipe = runQueuePipe(worker.stdout, None, self.cfgData, self, rqexec)
1258
1259 workerdata = {
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001260 "taskdeps" : self.rqdata.dataCaches[mc].task_deps,
1261 "fakerootenv" : self.rqdata.dataCaches[mc].fakerootenv,
1262 "fakerootdirs" : self.rqdata.dataCaches[mc].fakerootdirs,
1263 "fakerootnoenv" : self.rqdata.dataCaches[mc].fakerootnoenv,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001264 "sigdata" : bb.parse.siggen.get_taskdata(),
Andrew Geissler82c905d2020-04-13 13:39:40 -05001265 "logdefaultlevel" : bb.msg.loggerDefaultLogLevel,
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001266 "build_verbose_shell" : self.cooker.configuration.build_verbose_shell,
1267 "build_verbose_stdout" : self.cooker.configuration.build_verbose_stdout,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001268 "logdefaultdomain" : bb.msg.loggerDefaultDomains,
1269 "prhost" : self.cooker.prhost,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001270 "buildname" : self.cfgData.getVar("BUILDNAME"),
1271 "date" : self.cfgData.getVar("DATE"),
1272 "time" : self.cfgData.getVar("TIME"),
Brad Bishopa34c0302019-09-23 22:34:48 -04001273 "hashservaddr" : self.cooker.hashservaddr,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001274 "umask" : self.cfgData.getVar("BB_DEFAULT_UMASK"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001275 }
1276
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001277 worker.stdin.write(b"<cookerconfig>" + pickle.dumps(self.cooker.configuration) + b"</cookerconfig>")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001278 worker.stdin.write(b"<extraconfigdata>" + pickle.dumps(self.cooker.extraconfigdata) + b"</extraconfigdata>")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001279 worker.stdin.write(b"<workerdata>" + pickle.dumps(workerdata) + b"</workerdata>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001280 worker.stdin.flush()
1281
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001282 return RunQueueWorker(worker, workerpipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001283
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001284 def _teardown_worker(self, worker):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001285 if not worker:
1286 return
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001287 logger.debug("Teardown for bitbake-worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001288 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001289 worker.process.stdin.write(b"<quit></quit>")
1290 worker.process.stdin.flush()
1291 worker.process.stdin.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001292 except IOError:
1293 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001294 while worker.process.returncode is None:
1295 worker.pipe.read()
1296 worker.process.poll()
1297 while worker.pipe.read():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001298 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001299 worker.pipe.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001300
1301 def start_worker(self):
1302 if self.worker:
1303 self.teardown_workers()
1304 self.teardown = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001305 for mc in self.rqdata.dataCaches:
1306 self.worker[mc] = self._start_worker(mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001307
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001308 def start_fakeworker(self, rqexec, mc):
1309 if not mc in self.fakeworker:
1310 self.fakeworker[mc] = self._start_worker(mc, True, rqexec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001311
1312 def teardown_workers(self):
1313 self.teardown = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001314 for mc in self.worker:
1315 self._teardown_worker(self.worker[mc])
1316 self.worker = {}
1317 for mc in self.fakeworker:
1318 self._teardown_worker(self.fakeworker[mc])
1319 self.fakeworker = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001320
1321 def read_workers(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001322 for mc in self.worker:
1323 self.worker[mc].pipe.read()
1324 for mc in self.fakeworker:
1325 self.fakeworker[mc].pipe.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001326
1327 def active_fds(self):
1328 fds = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001329 for mc in self.worker:
1330 fds.append(self.worker[mc].pipe.input)
1331 for mc in self.fakeworker:
1332 fds.append(self.fakeworker[mc].pipe.input)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001333 return fds
1334
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001335 def check_stamp_task(self, tid, taskname = None, recurse = False, cache = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001336 def get_timestamp(f):
1337 try:
1338 if not os.access(f, os.F_OK):
1339 return None
1340 return os.stat(f)[stat.ST_MTIME]
1341 except:
1342 return None
1343
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001344 (mc, fn, tn, taskfn) = split_tid_mcfn(tid)
1345 if taskname is None:
1346 taskname = tn
1347
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001348 if self.stamppolicy == "perfile":
1349 fulldeptree = False
1350 else:
1351 fulldeptree = True
1352 stampwhitelist = []
1353 if self.stamppolicy == "whitelist":
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001354 stampwhitelist = self.rqdata.stampfnwhitelist[mc]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001355
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001356 stampfile = bb.build.stampfile(taskname, self.rqdata.dataCaches[mc], taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001357
1358 # If the stamp is missing, it's not current
1359 if not os.access(stampfile, os.F_OK):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001360 logger.debug2("Stampfile %s not available", stampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001361 return False
1362 # If it's a 'nostamp' task, it's not current
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001363 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001364 if 'nostamp' in taskdep and taskname in taskdep['nostamp']:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001365 logger.debug2("%s.%s is nostamp\n", fn, taskname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001366 return False
1367
1368 if taskname != "do_setscene" and taskname.endswith("_setscene"):
1369 return True
1370
1371 if cache is None:
1372 cache = {}
1373
1374 iscurrent = True
1375 t1 = get_timestamp(stampfile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001376 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001377 if iscurrent:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001378 (mc2, fn2, taskname2, taskfn2) = split_tid_mcfn(dep)
1379 stampfile2 = bb.build.stampfile(taskname2, self.rqdata.dataCaches[mc2], taskfn2)
1380 stampfile3 = bb.build.stampfile(taskname2 + "_setscene", self.rqdata.dataCaches[mc2], taskfn2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001381 t2 = get_timestamp(stampfile2)
1382 t3 = get_timestamp(stampfile3)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001383 if t3 and not t2:
1384 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001385 if t3 and t3 > t2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001386 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001387 if fn == fn2 or (fulldeptree and fn2 not in stampwhitelist):
1388 if not t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001389 logger.debug2('Stampfile %s does not exist', stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001390 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001391 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001392 if t1 < t2:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001393 logger.debug2('Stampfile %s < %s', stampfile, stampfile2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001394 iscurrent = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001395 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001396 if recurse and iscurrent:
1397 if dep in cache:
1398 iscurrent = cache[dep]
1399 if not iscurrent:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001400 logger.debug2('Stampfile for dependency %s:%s invalid (cached)' % (fn2, taskname2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001401 else:
1402 iscurrent = self.check_stamp_task(dep, recurse=True, cache=cache)
1403 cache[dep] = iscurrent
1404 if recurse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001405 cache[tid] = iscurrent
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001406 return iscurrent
1407
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001408 def validate_hashes(self, tocheck, data, currentcount=0, siginfo=False, summary=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04001409 valid = set()
1410 if self.hashvalidate:
Brad Bishop08902b02019-08-20 09:16:51 -04001411 sq_data = {}
1412 sq_data['hash'] = {}
1413 sq_data['hashfn'] = {}
1414 sq_data['unihash'] = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04001415 for tid in tocheck:
1416 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04001417 sq_data['hash'][tid] = self.rqdata.runtaskentries[tid].hash
1418 sq_data['hashfn'][tid] = self.rqdata.dataCaches[mc].hashfn[taskfn]
1419 sq_data['unihash'][tid] = self.rqdata.runtaskentries[tid].unihash
Brad Bishop96ff1982019-08-19 13:50:42 -04001420
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001421 valid = self.validate_hash(sq_data, data, siginfo, currentcount, summary)
Brad Bishop96ff1982019-08-19 13:50:42 -04001422
1423 return valid
1424
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001425 def validate_hash(self, sq_data, d, siginfo, currentcount, summary):
1426 locs = {"sq_data" : sq_data, "d" : d, "siginfo" : siginfo, "currentcount" : currentcount, "summary" : summary}
Brad Bishop19323692019-04-05 15:28:33 -04001427
Brad Bishop08902b02019-08-20 09:16:51 -04001428 # Metadata has **kwargs so args can be added, sq_data can also gain new fields
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001429 call = self.hashvalidate + "(sq_data, d, siginfo=siginfo, currentcount=currentcount, summary=summary)"
Brad Bishop19323692019-04-05 15:28:33 -04001430
Brad Bishop19323692019-04-05 15:28:33 -04001431 return bb.utils.better_eval(call, locs)
1432
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001433 def _execute_runqueue(self):
1434 """
1435 Run the tasks in a queue prepared by rqdata.prepare()
1436 Upon failure, optionally try to recover the build using any alternate providers
1437 (if the abort on failure configuration option isn't set)
1438 """
1439
1440 retval = True
1441
1442 if self.state is runQueuePrepare:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001443 # NOTE: if you add, remove or significantly refactor the stages of this
1444 # process then you should recalculate the weightings here. This is quite
1445 # easy to do - just change the next line temporarily to pass debug=True as
1446 # the last parameter and you'll get a printout of the weightings as well
1447 # as a map to the lines where next_stage() was called. Of course this isn't
1448 # critical, but it helps to keep the progress reporting accurate.
1449 self.rqdata.init_progress_reporter = bb.progress.MultiStageProcessProgressReporter(self.cooker.data,
1450 "Initialising tasks",
1451 [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 -05001452 if self.rqdata.prepare() == 0:
1453 self.state = runQueueComplete
1454 else:
1455 self.state = runQueueSceneInit
Brad Bishop00e122a2019-10-05 11:10:57 -04001456 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001457
1458 if self.state is runQueueSceneInit:
Brad Bishop96ff1982019-08-19 13:50:42 -04001459 self.rqdata.init_progress_reporter.next_stage()
1460
1461 # we are ready to run, emit dependency info to any UI or class which
1462 # needs it
1463 depgraph = self.cooker.buildDependTree(self, self.rqdata.taskData)
1464 self.rqdata.init_progress_reporter.next_stage()
1465 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.cooker.data)
1466
Brad Bishope2d5b612018-11-23 10:55:50 +13001467 if not self.dm_event_handler_registered:
1468 res = bb.event.register(self.dm_event_handler_name,
Brad Bishop96ff1982019-08-19 13:50:42 -04001469 lambda x: self.dm.check(self) if self.state in [runQueueRunning, runQueueCleanUp] else False,
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001470 ('bb.event.HeartbeatEvent',), data=self.cfgData)
Brad Bishope2d5b612018-11-23 10:55:50 +13001471 self.dm_event_handler_registered = True
1472
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001473 dump = self.cooker.configuration.dump_signatures
1474 if dump:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001475 self.rqdata.init_progress_reporter.finish()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001476 if 'printdiff' in dump:
1477 invalidtasks = self.print_diffscenetasks()
1478 self.dump_signatures(dump)
1479 if 'printdiff' in dump:
1480 self.write_diffscenetasks(invalidtasks)
1481 self.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001482
Brad Bishop96ff1982019-08-19 13:50:42 -04001483 if self.state is runQueueSceneInit:
1484 self.rqdata.init_progress_reporter.next_stage()
1485 self.start_worker()
1486 self.rqdata.init_progress_reporter.next_stage()
1487 self.rqexe = RunQueueExecute(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001488
Brad Bishop96ff1982019-08-19 13:50:42 -04001489 # If we don't have any setscene functions, skip execution
1490 if len(self.rqdata.runq_setscene_tids) == 0:
1491 logger.info('No setscene tasks')
1492 for tid in self.rqdata.runtaskentries:
1493 if len(self.rqdata.runtaskentries[tid].depends) == 0:
1494 self.rqexe.setbuildable(tid)
1495 self.rqexe.tasks_notcovered.add(tid)
1496 self.rqexe.sqdone = True
1497 logger.info('Executing Tasks')
1498 self.state = runQueueRunning
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001499
1500 if self.state is runQueueRunning:
1501 retval = self.rqexe.execute()
1502
1503 if self.state is runQueueCleanUp:
1504 retval = self.rqexe.finish()
1505
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001506 build_done = self.state is runQueueComplete or self.state is runQueueFailed
1507
1508 if build_done and self.dm_event_handler_registered:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -06001509 bb.event.remove(self.dm_event_handler_name, None, data=self.cfgData)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001510 self.dm_event_handler_registered = False
1511
1512 if build_done and self.rqexe:
Brad Bishop08902b02019-08-20 09:16:51 -04001513 bb.parse.siggen.save_unitaskhashes()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001514 self.teardown_workers()
Brad Bishop96ff1982019-08-19 13:50:42 -04001515 if self.rqexe:
1516 if self.rqexe.stats.failed:
1517 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)
1518 else:
1519 # Let's avoid the word "failed" if nothing actually did
1520 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 -05001521
1522 if self.state is runQueueFailed:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001523 raise bb.runqueue.TaskFailure(self.rqexe.failed_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001524
1525 if self.state is runQueueComplete:
1526 # All done
1527 return False
1528
1529 # Loop
1530 return retval
1531
1532 def execute_runqueue(self):
1533 # Catch unexpected exceptions and ensure we exit when an error occurs, not loop.
1534 try:
1535 return self._execute_runqueue()
1536 except bb.runqueue.TaskFailure:
1537 raise
1538 except SystemExit:
1539 raise
1540 except bb.BBHandledException:
1541 try:
1542 self.teardown_workers()
1543 except:
1544 pass
1545 self.state = runQueueComplete
1546 raise
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001547 except Exception as err:
1548 logger.exception("An uncaught exception occurred in runqueue")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001549 try:
1550 self.teardown_workers()
1551 except:
1552 pass
1553 self.state = runQueueComplete
1554 raise
1555
1556 def finish_runqueue(self, now = False):
1557 if not self.rqexe:
1558 self.state = runQueueComplete
1559 return
1560
1561 if now:
1562 self.rqexe.finish_now()
1563 else:
1564 self.rqexe.finish()
1565
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001566 def rq_dump_sigfn(self, fn, options):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001567 bb_cache = bb.cache.NoCache(self.cooker.databuilder)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001568 mc = bb.runqueue.mc_from_tid(fn)
1569 the_data = bb_cache.loadDataFull(fn, self.cooker.collections[mc].get_file_appends(fn))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001570 siggen = bb.parse.siggen
1571 dataCaches = self.rqdata.dataCaches
1572 siggen.dump_sigfn(fn, dataCaches, options)
1573
1574 def dump_signatures(self, options):
1575 fns = set()
1576 bb.note("Reparsing files to collect dependency data")
1577
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001578 for tid in self.rqdata.runtaskentries:
1579 fn = fn_from_tid(tid)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001580 fns.add(fn)
1581
1582 max_process = int(self.cfgData.getVar("BB_NUMBER_PARSE_THREADS") or os.cpu_count() or 1)
1583 # We cannot use the real multiprocessing.Pool easily due to some local data
1584 # that can't be pickled. This is a cheap multi-process solution.
1585 launched = []
1586 while fns:
1587 if len(launched) < max_process:
1588 p = Process(target=self.rq_dump_sigfn, args=(fns.pop(), options))
1589 p.start()
1590 launched.append(p)
1591 for q in launched:
1592 # The finished processes are joined when calling is_alive()
1593 if not q.is_alive():
1594 launched.remove(q)
1595 for p in launched:
1596 p.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001597
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001598 bb.parse.siggen.dump_sigs(self.rqdata.dataCaches, options)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001599
1600 return
1601
1602 def print_diffscenetasks(self):
1603
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001604 noexec = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001605 tocheck = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001606
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001607 for tid in self.rqdata.runtaskentries:
1608 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1609 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001610
1611 if 'noexec' in taskdep and taskname in taskdep['noexec']:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001612 noexec.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001613 continue
1614
Brad Bishop96ff1982019-08-19 13:50:42 -04001615 tocheck.add(tid)
Brad Bishop19323692019-04-05 15:28:33 -04001616
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001617 valid_new = self.validate_hashes(tocheck, self.cooker.data, 0, True, summary=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001618
1619 # Tasks which are both setscene and noexec never care about dependencies
1620 # We therefore find tasks which are setscene and noexec and mark their
1621 # unique dependencies as valid.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001622 for tid in noexec:
1623 if tid not in self.rqdata.runq_setscene_tids:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001624 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001625 for dep in self.rqdata.runtaskentries[tid].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001626 hasnoexecparents = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001627 for dep2 in self.rqdata.runtaskentries[dep].revdeps:
1628 if dep2 in self.rqdata.runq_setscene_tids and dep2 in noexec:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001629 continue
1630 hasnoexecparents = False
1631 break
1632 if hasnoexecparents:
1633 valid_new.add(dep)
1634
1635 invalidtasks = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001636 for tid in self.rqdata.runtaskentries:
1637 if tid not in valid_new and tid not in noexec:
1638 invalidtasks.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001639
1640 found = set()
1641 processed = set()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001642 for tid in invalidtasks:
1643 toprocess = set([tid])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001644 while toprocess:
1645 next = set()
1646 for t in toprocess:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001647 for dep in self.rqdata.runtaskentries[t].depends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001648 if dep in invalidtasks:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001649 found.add(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001650 if dep not in processed:
1651 processed.add(dep)
1652 next.add(dep)
1653 toprocess = next
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001654 if tid in found:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001655 toprocess = set()
1656
1657 tasklist = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001658 for tid in invalidtasks.difference(found):
1659 tasklist.append(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001660
1661 if tasklist:
1662 bb.plain("The differences between the current build and any cached tasks start at the following tasks:\n" + "\n".join(tasklist))
1663
1664 return invalidtasks.difference(found)
1665
1666 def write_diffscenetasks(self, invalidtasks):
1667
1668 # Define recursion callback
1669 def recursecb(key, hash1, hash2):
1670 hashes = [hash1, hash2]
1671 hashfiles = bb.siggen.find_siginfo(key, None, hashes, self.cfgData)
1672
1673 recout = []
1674 if len(hashfiles) == 2:
1675 out2 = bb.siggen.compare_sigfiles(hashfiles[hash1], hashfiles[hash2], recursecb)
Brad Bishopc342db32019-05-15 21:57:59 -04001676 recout.extend(list(' ' + l for l in out2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001677 else:
1678 recout.append("Unable to find matching sigdata for %s with hashes %s or %s" % (key, hash1, hash2))
1679
1680 return recout
1681
1682
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001683 for tid in invalidtasks:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001684 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
1685 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001686 h = self.rqdata.runtaskentries[tid].hash
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001687 matches = bb.siggen.find_siginfo(pn, taskname, [], self.cfgData)
1688 match = None
1689 for m in matches:
1690 if h in m:
1691 match = m
1692 if match is None:
1693 bb.fatal("Can't find a task we're supposed to have written out? (hash: %s)?" % h)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001694 matches = {k : v for k, v in iter(matches.items()) if h not in k}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001695 if matches:
1696 latestmatch = sorted(matches.keys(), key=lambda f: matches[f])[-1]
Brad Bishop19323692019-04-05 15:28:33 -04001697 prevh = __find_sha256__.search(latestmatch).group(0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001698 output = bb.siggen.compare_sigfiles(latestmatch, match, recursecb)
1699 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))
1700
Brad Bishop96ff1982019-08-19 13:50:42 -04001701
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001702class RunQueueExecute:
1703
1704 def __init__(self, rq):
1705 self.rq = rq
1706 self.cooker = rq.cooker
1707 self.cfgData = rq.cfgData
1708 self.rqdata = rq.rqdata
1709
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001710 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
1711 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001712
Brad Bishop96ff1982019-08-19 13:50:42 -04001713 self.sq_buildable = set()
1714 self.sq_running = set()
1715 self.sq_live = set()
1716
Brad Bishop08902b02019-08-20 09:16:51 -04001717 self.updated_taskhash_queue = []
1718 self.pending_migrations = set()
1719
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001720 self.runq_buildable = set()
1721 self.runq_running = set()
1722 self.runq_complete = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001723 self.runq_tasksrun = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001724
1725 self.build_stamps = {}
1726 self.build_stamps2 = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001727 self.failed_tids = []
Brad Bishop96ff1982019-08-19 13:50:42 -04001728 self.sq_deferred = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001729
1730 self.stampcache = {}
1731
Brad Bishop08902b02019-08-20 09:16:51 -04001732 self.holdoff_tasks = set()
Brad Bishopc68388fc2019-08-26 01:33:31 -04001733 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04001734 self.sqdone = False
1735
1736 self.stats = RunQueueStats(len(self.rqdata.runtaskentries))
1737 self.sq_stats = RunQueueStats(len(self.rqdata.runq_setscene_tids))
1738
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001739 for mc in rq.worker:
1740 rq.worker[mc].pipe.setrunqueueexec(self)
1741 for mc in rq.fakeworker:
1742 rq.fakeworker[mc].pipe.setrunqueueexec(self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001743
1744 if self.number_tasks <= 0:
1745 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks)
1746
Brad Bishop96ff1982019-08-19 13:50:42 -04001747 # List of setscene tasks which we've covered
1748 self.scenequeue_covered = set()
1749 # List of tasks which are covered (including setscene ones)
1750 self.tasks_covered = set()
1751 self.tasks_scenequeue_done = set()
1752 self.scenequeue_notcovered = set()
1753 self.tasks_notcovered = set()
1754 self.scenequeue_notneeded = set()
1755
Brad Bishop08902b02019-08-20 09:16:51 -04001756 # We can't skip specified target tasks which aren't setscene tasks
1757 self.cantskip = set(self.rqdata.target_tids)
1758 self.cantskip.difference_update(self.rqdata.runq_setscene_tids)
1759 self.cantskip.intersection_update(self.rqdata.runtaskentries)
Brad Bishop96ff1982019-08-19 13:50:42 -04001760
1761 schedulers = self.get_schedulers()
1762 for scheduler in schedulers:
1763 if self.scheduler == scheduler.name:
1764 self.sched = scheduler(self, self.rqdata)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001765 logger.debug("Using runqueue scheduler '%s'", scheduler.name)
Brad Bishop96ff1982019-08-19 13:50:42 -04001766 break
1767 else:
1768 bb.fatal("Invalid scheduler '%s'. Available schedulers: %s" %
1769 (self.scheduler, ", ".join(obj.name for obj in schedulers)))
1770
Brad Bishop08902b02019-08-20 09:16:51 -04001771 #if len(self.rqdata.runq_setscene_tids) > 0:
1772 self.sqdata = SQData()
1773 build_scenequeue_data(self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self)
Brad Bishop96ff1982019-08-19 13:50:42 -04001774
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001775 def runqueue_process_waitpid(self, task, status):
1776
1777 # self.build_stamps[pid] may not exist when use shared work directory.
1778 if task in self.build_stamps:
1779 self.build_stamps2.remove(self.build_stamps[task])
1780 del self.build_stamps[task]
1781
Brad Bishop96ff1982019-08-19 13:50:42 -04001782 if task in self.sq_live:
1783 if status != 0:
1784 self.sq_task_fail(task, status)
1785 else:
1786 self.sq_task_complete(task)
1787 self.sq_live.remove(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001788 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04001789 if status != 0:
1790 self.task_fail(task, status)
1791 else:
1792 self.task_complete(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001793 return True
1794
1795 def finish_now(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001796 for mc in self.rq.worker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001797 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001798 self.rq.worker[mc].process.stdin.write(b"<finishnow></finishnow>")
1799 self.rq.worker[mc].process.stdin.flush()
1800 except IOError:
1801 # worker must have died?
1802 pass
1803 for mc in self.rq.fakeworker:
1804 try:
1805 self.rq.fakeworker[mc].process.stdin.write(b"<finishnow></finishnow>")
1806 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001807 except IOError:
1808 # worker must have died?
1809 pass
1810
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001811 if len(self.failed_tids) != 0:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001812 self.rq.state = runQueueFailed
1813 return
1814
1815 self.rq.state = runQueueComplete
1816 return
1817
1818 def finish(self):
1819 self.rq.state = runQueueCleanUp
1820
Brad Bishop96ff1982019-08-19 13:50:42 -04001821 active = self.stats.active + self.sq_stats.active
1822 if active > 0:
1823 bb.event.fire(runQueueExitWait(active), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001824 self.rq.read_workers()
1825 return self.rq.active_fds()
1826
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001827 if len(self.failed_tids) != 0:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001828 self.rq.state = runQueueFailed
1829 return True
1830
1831 self.rq.state = runQueueComplete
1832 return True
1833
Brad Bishop96ff1982019-08-19 13:50:42 -04001834 # Used by setscene only
1835 def check_dependencies(self, task, taskdeps):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001836 if not self.rq.depvalidate:
1837 return False
1838
Brad Bishop08902b02019-08-20 09:16:51 -04001839 # Must not edit parent data
1840 taskdeps = set(taskdeps)
1841
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001842 taskdata = {}
1843 taskdeps.add(task)
1844 for dep in taskdeps:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001845 (mc, fn, taskname, taskfn) = split_tid_mcfn(dep)
1846 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001847 taskdata[dep] = [pn, taskname, fn]
1848 call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001849 locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001850 valid = bb.utils.better_eval(call, locs)
1851 return valid
1852
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001853 def can_start_task(self):
Brad Bishop96ff1982019-08-19 13:50:42 -04001854 active = self.stats.active + self.sq_stats.active
1855 can_start = active < self.number_tasks
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001856 return can_start
1857
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001858 def get_schedulers(self):
1859 schedulers = set(obj for obj in globals().values()
1860 if type(obj) is type and
1861 issubclass(obj, RunQueueScheduler))
1862
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001863 user_schedulers = self.cfgData.getVar("BB_SCHEDULERS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001864 if user_schedulers:
1865 for sched in user_schedulers.split():
1866 if not "." in sched:
1867 bb.note("Ignoring scheduler '%s' from BB_SCHEDULERS: not an import" % sched)
1868 continue
1869
1870 modname, name = sched.rsplit(".", 1)
1871 try:
1872 module = __import__(modname, fromlist=(name,))
1873 except ImportError as exc:
1874 logger.critical("Unable to import scheduler '%s' from '%s': %s" % (name, modname, exc))
1875 raise SystemExit(1)
1876 else:
1877 schedulers.add(getattr(module, name))
1878 return schedulers
1879
1880 def setbuildable(self, task):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001881 self.runq_buildable.add(task)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001882 self.sched.newbuildable(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001883
1884 def task_completeoutright(self, task):
1885 """
1886 Mark a task as completed
1887 Look at the reverse dependencies and mark any task with
1888 completed dependencies as buildable
1889 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001890 self.runq_complete.add(task)
1891 for revdep in self.rqdata.runtaskentries[task].revdeps:
1892 if revdep in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001893 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001894 if revdep in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001895 continue
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001896 alldeps = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001897 for dep in self.rqdata.runtaskentries[revdep].depends:
1898 if dep not in self.runq_complete:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001899 alldeps = False
1900 break
1901 if alldeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001902 self.setbuildable(revdep)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001903 logger.debug("Marking task %s as buildable", revdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001904
1905 def task_complete(self, task):
1906 self.stats.taskCompleted()
1907 bb.event.fire(runQueueTaskCompleted(task, self.stats, self.rq), self.cfgData)
1908 self.task_completeoutright(task)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001909 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001910
1911 def task_fail(self, task, exitcode):
1912 """
1913 Called when a task has failed
1914 Updates the state engine with the failure
1915 """
1916 self.stats.taskFailed()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001917 self.failed_tids.append(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001918 bb.event.fire(runQueueTaskFailed(task, self.stats, exitcode, self.rq), self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001919 if self.rqdata.taskData[''].abort:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001920 self.rq.state = runQueueCleanUp
1921
1922 def task_skip(self, task, reason):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001923 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001924 self.setbuildable(task)
1925 bb.event.fire(runQueueTaskSkipped(task, self.stats, self.rq, reason), self.cfgData)
1926 self.task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001927 self.stats.taskSkipped()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001928 self.stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001929
Brad Bishop08902b02019-08-20 09:16:51 -04001930 def summarise_scenequeue_errors(self):
1931 err = False
1932 if not self.sqdone:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001933 logger.debug('We could skip tasks %s', "\n".join(sorted(self.scenequeue_covered)))
Brad Bishop08902b02019-08-20 09:16:51 -04001934 completeevent = sceneQueueComplete(self.sq_stats, self.rq)
1935 bb.event.fire(completeevent, self.cfgData)
1936 if self.sq_deferred:
1937 logger.error("Scenequeue had deferred entries: %s" % pprint.pformat(self.sq_deferred))
1938 err = True
1939 if self.updated_taskhash_queue:
1940 logger.error("Scenequeue had unprocessed changed taskhash entries: %s" % pprint.pformat(self.updated_taskhash_queue))
1941 err = True
1942 if self.holdoff_tasks:
1943 logger.error("Scenequeue had holdoff tasks: %s" % pprint.pformat(self.holdoff_tasks))
1944 err = True
1945
1946 for tid in self.rqdata.runq_setscene_tids:
1947 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
1948 err = True
1949 logger.error("Setscene Task %s was never marked as covered or not covered" % tid)
1950 if tid not in self.sq_buildable:
1951 err = True
1952 logger.error("Setscene Task %s was never marked as buildable" % tid)
1953 if tid not in self.sq_running:
1954 err = True
1955 logger.error("Setscene Task %s was never marked as running" % tid)
1956
1957 for x in self.rqdata.runtaskentries:
1958 if x not in self.tasks_covered and x not in self.tasks_notcovered:
1959 logger.error("Task %s was never moved from the setscene queue" % x)
1960 err = True
1961 if x not in self.tasks_scenequeue_done:
1962 logger.error("Task %s was never processed by the setscene code" % x)
1963 err = True
1964 if len(self.rqdata.runtaskentries[x].depends) == 0 and x not in self.runq_buildable:
1965 logger.error("Task %s was never marked as buildable by the setscene code" % x)
1966 err = True
1967 return err
1968
1969
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001970 def execute(self):
1971 """
Brad Bishop96ff1982019-08-19 13:50:42 -04001972 Run the tasks in a queue prepared by prepare_runqueue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001973 """
1974
1975 self.rq.read_workers()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001976 if self.updated_taskhash_queue or self.pending_migrations:
1977 self.process_possible_migrations()
1978
1979 if not hasattr(self, "sorted_setscene_tids"):
1980 # Don't want to sort this set every execution
1981 self.sorted_setscene_tids = sorted(self.rqdata.runq_setscene_tids)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001982
Brad Bishop96ff1982019-08-19 13:50:42 -04001983 task = None
1984 if not self.sqdone and self.can_start_task():
1985 # Find the next setscene to run
Andrew Geissler82c905d2020-04-13 13:39:40 -05001986 for nexttask in self.sorted_setscene_tids:
Brad Bishop96ff1982019-08-19 13:50:42 -04001987 if nexttask in self.sq_buildable and nexttask not in self.sq_running and self.sqdata.stamps[nexttask] not in self.build_stamps.values():
1988 if nexttask not in self.sqdata.unskippable and len(self.sqdata.sq_revdeps[nexttask]) > 0 and self.sqdata.sq_revdeps[nexttask].issubset(self.scenequeue_covered) and self.check_dependencies(nexttask, self.sqdata.sq_revdeps[nexttask]):
1989 if nexttask not in self.rqdata.target_tids:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001990 logger.debug2("Skipping setscene for task %s" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04001991 self.sq_task_skip(nexttask)
1992 self.scenequeue_notneeded.add(nexttask)
1993 if nexttask in self.sq_deferred:
1994 del self.sq_deferred[nexttask]
1995 return True
Brad Bishop08902b02019-08-20 09:16:51 -04001996 # If covered tasks are running, need to wait for them to complete
1997 for t in self.sqdata.sq_covered_tasks[nexttask]:
1998 if t in self.runq_running and t not in self.runq_complete:
1999 continue
Brad Bishop96ff1982019-08-19 13:50:42 -04002000 if nexttask in self.sq_deferred:
2001 if self.sq_deferred[nexttask] not in self.runq_complete:
2002 continue
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002003 logger.debug("Task %s no longer deferred" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002004 del self.sq_deferred[nexttask]
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002005 valid = self.rq.validate_hashes(set([nexttask]), self.cooker.data, 0, False, summary=False)
Brad Bishop96ff1982019-08-19 13:50:42 -04002006 if not valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002007 logger.debug("%s didn't become valid, skipping setscene" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002008 self.sq_task_failoutright(nexttask)
2009 return True
2010 else:
2011 self.sqdata.outrightfail.remove(nexttask)
2012 if nexttask in self.sqdata.outrightfail:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002013 logger.debug2('No package found, so skipping setscene task %s', nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002014 self.sq_task_failoutright(nexttask)
2015 return True
2016 if nexttask in self.sqdata.unskippable:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002017 logger.debug2("Setscene task %s is unskippable" % nexttask)
Brad Bishop96ff1982019-08-19 13:50:42 -04002018 task = nexttask
2019 break
2020 if task is not None:
2021 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
2022 taskname = taskname + "_setscene"
2023 if self.rq.check_stamp_task(task, taskname_from_tid(task), recurse = True, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002024 logger.debug2('Stamp for underlying task %s is current, so skipping setscene variant', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002025 self.sq_task_failoutright(task)
2026 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002027
Brad Bishop96ff1982019-08-19 13:50:42 -04002028 if self.cooker.configuration.force:
2029 if task in self.rqdata.target_tids:
2030 self.sq_task_failoutright(task)
2031 return True
2032
2033 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002034 logger.debug2('Setscene stamp current task %s, so skip it and its dependencies', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002035 self.sq_task_skip(task)
2036 return True
2037
2038 if self.cooker.configuration.skipsetscene:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002039 logger.debug2('No setscene tasks should be executed. Skipping %s', task)
Brad Bishop96ff1982019-08-19 13:50:42 -04002040 self.sq_task_failoutright(task)
2041 return True
2042
2043 startevent = sceneQueueTaskStarted(task, self.sq_stats, self.rq)
2044 bb.event.fire(startevent, self.cfgData)
2045
2046 taskdepdata = self.sq_build_taskdepdata(task)
2047
2048 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2049 taskhash = self.rqdata.get_task_hash(task)
2050 unihash = self.rqdata.get_task_unihash(task)
2051 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
2052 if not mc in self.rq.fakeworker:
2053 self.rq.start_fakeworker(self, mc)
Andrew Geissler5a43b432020-06-13 10:46:56 -05002054 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 -04002055 self.rq.fakeworker[mc].process.stdin.flush()
2056 else:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002057 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 -04002058 self.rq.worker[mc].process.stdin.flush()
2059
2060 self.build_stamps[task] = bb.build.stampfile(taskname, self.rqdata.dataCaches[mc], taskfn, noextra=True)
2061 self.build_stamps2.append(self.build_stamps[task])
2062 self.sq_running.add(task)
2063 self.sq_live.add(task)
2064 self.sq_stats.taskActive()
2065 if self.can_start_task():
2066 return True
2067
Brad Bishopc68388fc2019-08-26 01:33:31 -04002068 self.update_holdofftasks()
2069
Brad Bishop08902b02019-08-20 09:16:51 -04002070 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 -05002071 hashequiv_logger.verbose("Setscene tasks completed")
Brad Bishop96ff1982019-08-19 13:50:42 -04002072
Brad Bishop08902b02019-08-20 09:16:51 -04002073 err = self.summarise_scenequeue_errors()
Brad Bishop96ff1982019-08-19 13:50:42 -04002074 if err:
2075 self.rq.state = runQueueFailed
2076 return True
2077
2078 if self.cooker.configuration.setsceneonly:
2079 self.rq.state = runQueueComplete
2080 return True
2081 self.sqdone = True
2082
2083 if self.stats.total == 0:
2084 # nothing to do
2085 self.rq.state = runQueueComplete
2086 return True
2087
2088 if self.cooker.configuration.setsceneonly:
2089 task = None
2090 else:
2091 task = self.sched.next()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002092 if task is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002093 (mc, fn, taskname, taskfn) = split_tid_mcfn(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002094
Brad Bishop96ff1982019-08-19 13:50:42 -04002095 if self.rqdata.setscenewhitelist is not None:
2096 if self.check_setscenewhitelist(task):
2097 self.task_fail(task, "setscene whitelist")
2098 return True
2099
2100 if task in self.tasks_covered:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002101 logger.debug2("Setscene covered task %s", task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002102 self.task_skip(task, "covered")
2103 return True
2104
2105 if self.rq.check_stamp_task(task, taskname, cache=self.stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002106 logger.debug2("Stamp current task %s", task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002107
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002108 self.task_skip(task, "existing")
Andrew Geissler82c905d2020-04-13 13:39:40 -05002109 self.runq_tasksrun.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002110 return True
2111
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002112 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002113 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2114 startevent = runQueueTaskStarted(task, self.stats, self.rq,
2115 noexec=True)
2116 bb.event.fire(startevent, self.cfgData)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002117 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002118 self.stats.taskActive()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002119 if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002120 bb.build.make_stamp(taskname, self.rqdata.dataCaches[mc], taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002121 self.task_complete(task)
2122 return True
2123 else:
2124 startevent = runQueueTaskStarted(task, self.stats, self.rq)
2125 bb.event.fire(startevent, self.cfgData)
2126
2127 taskdepdata = self.build_taskdepdata(task)
2128
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002129 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
Brad Bishop19323692019-04-05 15:28:33 -04002130 taskhash = self.rqdata.get_task_hash(task)
2131 unihash = self.rqdata.get_task_unihash(task)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002132 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 -05002133 if not mc in self.rq.fakeworker:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002134 try:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002135 self.rq.start_fakeworker(self, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002136 except OSError as exc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002137 logger.critical("Failed to spawn fakeroot worker to run %s: %s" % (task, str(exc)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002138 self.rq.state = runQueueFailed
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002139 self.stats.taskFailed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002140 return True
Andrew Geissler5a43b432020-06-13 10:46:56 -05002141 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 -06002142 self.rq.fakeworker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002143 else:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002144 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 -06002145 self.rq.worker[mc].process.stdin.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002146
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002147 self.build_stamps[task] = bb.build.stampfile(taskname, self.rqdata.dataCaches[mc], taskfn, noextra=True)
2148 self.build_stamps2.append(self.build_stamps[task])
2149 self.runq_running.add(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002150 self.stats.taskActive()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08002151 if self.can_start_task():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002152 return True
2153
Brad Bishop96ff1982019-08-19 13:50:42 -04002154 if self.stats.active > 0 or self.sq_stats.active > 0:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002155 self.rq.read_workers()
2156 return self.rq.active_fds()
2157
Brad Bishop96ff1982019-08-19 13:50:42 -04002158 # No more tasks can be run. If we have deferred setscene tasks we should run them.
2159 if self.sq_deferred:
2160 tid = self.sq_deferred.pop(list(self.sq_deferred.keys())[0])
2161 logger.warning("Runqeueue deadlocked on deferred tasks, forcing task %s" % tid)
2162 self.sq_task_failoutright(tid)
2163 return True
2164
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002165 if len(self.failed_tids) != 0:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002166 self.rq.state = runQueueFailed
2167 return True
2168
2169 # Sanity Checks
Brad Bishop08902b02019-08-20 09:16:51 -04002170 err = self.summarise_scenequeue_errors()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002171 for task in self.rqdata.runtaskentries:
2172 if task not in self.runq_buildable:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002173 logger.error("Task %s never buildable!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002174 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002175 elif task not in self.runq_running:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002176 logger.error("Task %s never ran!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002177 err = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002178 elif task not in self.runq_complete:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002179 logger.error("Task %s never completed!", task)
Brad Bishop08902b02019-08-20 09:16:51 -04002180 err = True
2181
2182 if err:
2183 self.rq.state = runQueueFailed
2184 else:
2185 self.rq.state = runQueueComplete
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002186
2187 return True
2188
Brad Bishopc68388fc2019-08-26 01:33:31 -04002189 def filtermcdeps(self, task, mc, deps):
Andrew Geissler99467da2019-02-25 18:54:23 -06002190 ret = set()
Andrew Geissler99467da2019-02-25 18:54:23 -06002191 for dep in deps:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002192 thismc = mc_from_tid(dep)
2193 if thismc != mc:
Andrew Geissler99467da2019-02-25 18:54:23 -06002194 continue
2195 ret.add(dep)
2196 return ret
2197
Brad Bishopa34c0302019-09-23 22:34:48 -04002198 # We filter out multiconfig dependencies from taskdepdata we pass to the tasks
Andrew Geissler99467da2019-02-25 18:54:23 -06002199 # as most code can't handle them
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002200 def build_taskdepdata(self, task):
2201 taskdepdata = {}
Brad Bishopc68388fc2019-08-26 01:33:31 -04002202 mc = mc_from_tid(task)
Brad Bishop08902b02019-08-20 09:16:51 -04002203 next = self.rqdata.runtaskentries[task].depends.copy()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002204 next.add(task)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002205 next = self.filtermcdeps(task, mc, next)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002206 while next:
2207 additional = []
2208 for revdep in next:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002209 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2210 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2211 deps = self.rqdata.runtaskentries[revdep].depends
2212 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002213 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002214 unihash = self.rqdata.runtaskentries[revdep].unihash
Brad Bishopc68388fc2019-08-26 01:33:31 -04002215 deps = self.filtermcdeps(task, mc, deps)
Brad Bishop19323692019-04-05 15:28:33 -04002216 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002217 for revdep2 in deps:
2218 if revdep2 not in taskdepdata:
2219 additional.append(revdep2)
2220 next = additional
2221
2222 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2223 return taskdepdata
2224
Brad Bishop08902b02019-08-20 09:16:51 -04002225 def update_holdofftasks(self):
Brad Bishopc68388fc2019-08-26 01:33:31 -04002226
2227 if not self.holdoff_need_update:
2228 return
2229
2230 notcovered = set(self.scenequeue_notcovered)
2231 notcovered |= self.cantskip
2232 for tid in self.scenequeue_notcovered:
2233 notcovered |= self.sqdata.sq_covered_tasks[tid]
2234 notcovered |= self.sqdata.unskippable.difference(self.rqdata.runq_setscene_tids)
2235 notcovered.intersection_update(self.tasks_scenequeue_done)
2236
2237 covered = set(self.scenequeue_covered)
2238 for tid in self.scenequeue_covered:
2239 covered |= self.sqdata.sq_covered_tasks[tid]
2240 covered.difference_update(notcovered)
2241 covered.intersection_update(self.tasks_scenequeue_done)
2242
2243 for tid in notcovered | covered:
2244 if len(self.rqdata.runtaskentries[tid].depends) == 0:
2245 self.setbuildable(tid)
2246 elif self.rqdata.runtaskentries[tid].depends.issubset(self.runq_complete):
2247 self.setbuildable(tid)
2248
2249 self.tasks_covered = covered
2250 self.tasks_notcovered = notcovered
2251
Brad Bishop08902b02019-08-20 09:16:51 -04002252 self.holdoff_tasks = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002253
Brad Bishop08902b02019-08-20 09:16:51 -04002254 for tid in self.rqdata.runq_setscene_tids:
2255 if tid not in self.scenequeue_covered and tid not in self.scenequeue_notcovered:
2256 self.holdoff_tasks.add(tid)
2257
2258 for tid in self.holdoff_tasks.copy():
2259 for dep in self.sqdata.sq_covered_tasks[tid]:
2260 if dep not in self.runq_complete:
2261 self.holdoff_tasks.add(dep)
2262
Brad Bishopc68388fc2019-08-26 01:33:31 -04002263 self.holdoff_need_update = False
2264
Brad Bishop08902b02019-08-20 09:16:51 -04002265 def process_possible_migrations(self):
2266
2267 changed = set()
Andrew Geissler82c905d2020-04-13 13:39:40 -05002268 toprocess = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002269 for tid, unihash in self.updated_taskhash_queue.copy():
2270 if tid in self.runq_running and tid not in self.runq_complete:
2271 continue
2272
2273 self.updated_taskhash_queue.remove((tid, unihash))
2274
2275 if unihash != self.rqdata.runtaskentries[tid].unihash:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002276 hashequiv_logger.verbose("Task %s unihash changed to %s" % (tid, unihash))
Brad Bishop08902b02019-08-20 09:16:51 -04002277 self.rqdata.runtaskentries[tid].unihash = unihash
2278 bb.parse.siggen.set_unihash(tid, unihash)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002279 toprocess.add(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002280
Andrew Geissler82c905d2020-04-13 13:39:40 -05002281 # Work out all tasks which depend upon these
2282 total = set()
2283 next = set()
2284 for p in toprocess:
2285 next |= self.rqdata.runtaskentries[p].revdeps
2286 while next:
2287 current = next.copy()
2288 total = total | next
2289 next = set()
2290 for ntid in current:
2291 next |= self.rqdata.runtaskentries[ntid].revdeps
2292 next.difference_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002293
Andrew Geissler82c905d2020-04-13 13:39:40 -05002294 # Now iterate those tasks in dependency order to regenerate their taskhash/unihash
2295 next = set()
2296 for p in total:
2297 if len(self.rqdata.runtaskentries[p].depends) == 0:
2298 next.add(p)
2299 elif self.rqdata.runtaskentries[p].depends.isdisjoint(total):
2300 next.add(p)
2301
2302 # When an item doesn't have dependencies in total, we can process it. Drop items from total when handled
2303 while next:
2304 current = next.copy()
2305 next = set()
2306 for tid in current:
2307 if len(self.rqdata.runtaskentries[p].depends) and not self.rqdata.runtaskentries[tid].depends.isdisjoint(total):
2308 continue
2309 orighash = self.rqdata.runtaskentries[tid].hash
Andrew Geissler5a43b432020-06-13 10:46:56 -05002310 dc = bb.parse.siggen.get_data_caches(self.rqdata.dataCaches, mc_from_tid(tid))
2311 newhash = bb.parse.siggen.get_taskhash(tid, self.rqdata.runtaskentries[tid].depends, dc)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002312 origuni = self.rqdata.runtaskentries[tid].unihash
2313 newuni = bb.parse.siggen.get_unihash(tid)
2314 # FIXME, need to check it can come from sstate at all for determinism?
2315 remapped = False
2316 if newuni == origuni:
2317 # Nothing to do, we match, skip code below
2318 remapped = True
2319 elif tid in self.scenequeue_covered or tid in self.sq_live:
2320 # Already ran this setscene task or it running. Report the new taskhash
2321 bb.parse.siggen.report_unihash_equiv(tid, newhash, origuni, newuni, self.rqdata.dataCaches)
2322 hashequiv_logger.verbose("Already covered setscene for %s so ignoring rehash (remap)" % (tid))
2323 remapped = True
2324
2325 if not remapped:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002326 #logger.debug("Task %s hash changes: %s->%s %s->%s" % (tid, orighash, newhash, origuni, newuni))
Andrew Geissler82c905d2020-04-13 13:39:40 -05002327 self.rqdata.runtaskentries[tid].hash = newhash
2328 self.rqdata.runtaskentries[tid].unihash = newuni
2329 changed.add(tid)
2330
2331 next |= self.rqdata.runtaskentries[tid].revdeps
2332 total.remove(tid)
2333 next.intersection_update(total)
Brad Bishop08902b02019-08-20 09:16:51 -04002334
2335 if changed:
2336 for mc in self.rq.worker:
2337 self.rq.worker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2338 for mc in self.rq.fakeworker:
2339 self.rq.fakeworker[mc].process.stdin.write(b"<newtaskhashes>" + pickle.dumps(bb.parse.siggen.get_taskhashes()) + b"</newtaskhashes>")
2340
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002341 hashequiv_logger.debug(pprint.pformat("Tasks changed:\n%s" % (changed)))
Brad Bishop08902b02019-08-20 09:16:51 -04002342
2343 for tid in changed:
2344 if tid not in self.rqdata.runq_setscene_tids:
2345 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002346 if tid not in self.pending_migrations:
2347 self.pending_migrations.add(tid)
2348
Andrew Geissler82c905d2020-04-13 13:39:40 -05002349 update_tasks = []
Brad Bishop08902b02019-08-20 09:16:51 -04002350 for tid in self.pending_migrations.copy():
Andrew Geissler82c905d2020-04-13 13:39:40 -05002351 if tid in self.runq_running or tid in self.sq_live:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002352 # Too late, task already running, not much we can do now
2353 self.pending_migrations.remove(tid)
2354 continue
2355
Brad Bishop08902b02019-08-20 09:16:51 -04002356 valid = True
2357 # Check no tasks this covers are running
2358 for dep in self.sqdata.sq_covered_tasks[tid]:
2359 if dep in self.runq_running and dep not in self.runq_complete:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002360 hashequiv_logger.debug2("Task %s is running which blocks setscene for %s from running" % (dep, tid))
Brad Bishop08902b02019-08-20 09:16:51 -04002361 valid = False
2362 break
2363 if not valid:
2364 continue
2365
2366 self.pending_migrations.remove(tid)
Brad Bishopc68388fc2019-08-26 01:33:31 -04002367 changed = True
Brad Bishop08902b02019-08-20 09:16:51 -04002368
2369 if tid in self.tasks_scenequeue_done:
2370 self.tasks_scenequeue_done.remove(tid)
2371 for dep in self.sqdata.sq_covered_tasks[tid]:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002372 if dep in self.runq_complete and dep not in self.runq_tasksrun:
2373 bb.error("Task %s marked as completed but now needing to rerun? Aborting build." % dep)
2374 self.failed_tids.append(tid)
2375 self.rq.state = runQueueCleanUp
2376 return
2377
Brad Bishop08902b02019-08-20 09:16:51 -04002378 if dep not in self.runq_complete:
2379 if dep in self.tasks_scenequeue_done and dep not in self.sqdata.unskippable:
2380 self.tasks_scenequeue_done.remove(dep)
2381
2382 if tid in self.sq_buildable:
2383 self.sq_buildable.remove(tid)
2384 if tid in self.sq_running:
2385 self.sq_running.remove(tid)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002386 harddepfail = False
2387 for t in self.sqdata.sq_harddeps:
2388 if tid in self.sqdata.sq_harddeps[t] and t in self.scenequeue_notcovered:
2389 harddepfail = True
2390 break
2391 if not harddepfail and self.sqdata.sq_revdeps[tid].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
Brad Bishop08902b02019-08-20 09:16:51 -04002392 if tid not in self.sq_buildable:
2393 self.sq_buildable.add(tid)
2394 if len(self.sqdata.sq_revdeps[tid]) == 0:
2395 self.sq_buildable.add(tid)
2396
2397 if tid in self.sqdata.outrightfail:
2398 self.sqdata.outrightfail.remove(tid)
2399 if tid in self.scenequeue_notcovered:
2400 self.scenequeue_notcovered.remove(tid)
2401 if tid in self.scenequeue_covered:
2402 self.scenequeue_covered.remove(tid)
2403 if tid in self.scenequeue_notneeded:
2404 self.scenequeue_notneeded.remove(tid)
2405
2406 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2407 self.sqdata.stamps[tid] = bb.build.stampfile(taskname + "_setscene", self.rqdata.dataCaches[mc], taskfn, noextra=True)
2408
2409 if tid in self.stampcache:
2410 del self.stampcache[tid]
2411
2412 if tid in self.build_stamps:
2413 del self.build_stamps[tid]
2414
Andrew Geissler82c905d2020-04-13 13:39:40 -05002415 update_tasks.append((tid, harddepfail, tid in self.sqdata.valid))
2416
2417 if update_tasks:
Brad Bishop08902b02019-08-20 09:16:51 -04002418 self.sqdone = False
Andrew Geissler82c905d2020-04-13 13:39:40 -05002419 update_scenequeue_data([t[0] for t in update_tasks], self.sqdata, self.rqdata, self.rq, self.cooker, self.stampcache, self, summary=False)
2420
2421 for (tid, harddepfail, origvalid) in update_tasks:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002422 if tid in self.sqdata.valid and not origvalid:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002423 hashequiv_logger.verbose("Setscene task %s became valid" % tid)
2424 if harddepfail:
2425 self.sq_task_failoutright(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002426
2427 if changed:
Brad Bishopc68388fc2019-08-26 01:33:31 -04002428 self.holdoff_need_update = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002429
Brad Bishop96ff1982019-08-19 13:50:42 -04002430 def scenequeue_updatecounters(self, task, fail=False):
Brad Bishop08902b02019-08-20 09:16:51 -04002431
2432 for dep in sorted(self.sqdata.sq_deps[task]):
Brad Bishop96ff1982019-08-19 13:50:42 -04002433 if fail and task in self.sqdata.sq_harddeps and dep in self.sqdata.sq_harddeps[task]:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002434 logger.debug2("%s was unavailable and is a hard dependency of %s so skipping" % (task, dep))
Brad Bishop96ff1982019-08-19 13:50:42 -04002435 self.sq_task_failoutright(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002436 continue
Brad Bishop08902b02019-08-20 09:16:51 -04002437 if self.sqdata.sq_revdeps[dep].issubset(self.scenequeue_covered | self.scenequeue_notcovered):
2438 if dep not in self.sq_buildable:
2439 self.sq_buildable.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002440
Brad Bishop96ff1982019-08-19 13:50:42 -04002441 next = set([task])
2442 while next:
2443 new = set()
Brad Bishop08902b02019-08-20 09:16:51 -04002444 for t in sorted(next):
Brad Bishop96ff1982019-08-19 13:50:42 -04002445 self.tasks_scenequeue_done.add(t)
2446 # Look down the dependency chain for non-setscene things which this task depends on
2447 # and mark as 'done'
2448 for dep in self.rqdata.runtaskentries[t].depends:
2449 if dep in self.rqdata.runq_setscene_tids or dep in self.tasks_scenequeue_done:
2450 continue
2451 if self.rqdata.runtaskentries[dep].revdeps.issubset(self.tasks_scenequeue_done):
2452 new.add(dep)
Brad Bishop96ff1982019-08-19 13:50:42 -04002453 next = new
2454
Brad Bishopc68388fc2019-08-26 01:33:31 -04002455 self.holdoff_need_update = True
Brad Bishop96ff1982019-08-19 13:50:42 -04002456
2457 def sq_task_completeoutright(self, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002458 """
2459 Mark a task as completed
2460 Look at the reverse dependencies and mark any task with
2461 completed dependencies as buildable
2462 """
2463
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002464 logger.debug('Found task %s which could be accelerated', task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002465 self.scenequeue_covered.add(task)
2466 self.scenequeue_updatecounters(task)
2467
Brad Bishop96ff1982019-08-19 13:50:42 -04002468 def sq_check_taskfail(self, task):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002469 if self.rqdata.setscenewhitelist is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002470 realtask = task.split('_setscene')[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -05002471 (mc, fn, taskname, taskfn) = split_tid_mcfn(realtask)
2472 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002473 if not check_setscene_enforce_whitelist(pn, taskname, self.rqdata.setscenewhitelist):
2474 logger.error('Task %s.%s failed' % (pn, taskname + "_setscene"))
2475 self.rq.state = runQueueCleanUp
2476
Brad Bishop96ff1982019-08-19 13:50:42 -04002477 def sq_task_complete(self, task):
2478 self.sq_stats.taskCompleted()
2479 bb.event.fire(sceneQueueTaskCompleted(task, self.sq_stats, self.rq), self.cfgData)
2480 self.sq_task_completeoutright(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002481
Brad Bishop96ff1982019-08-19 13:50:42 -04002482 def sq_task_fail(self, task, result):
2483 self.sq_stats.taskFailed()
2484 bb.event.fire(sceneQueueTaskFailed(task, self.sq_stats, result, self), self.cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002485 self.scenequeue_notcovered.add(task)
2486 self.scenequeue_updatecounters(task, True)
Brad Bishop96ff1982019-08-19 13:50:42 -04002487 self.sq_check_taskfail(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002488
Brad Bishop96ff1982019-08-19 13:50:42 -04002489 def sq_task_failoutright(self, task):
2490 self.sq_running.add(task)
2491 self.sq_buildable.add(task)
2492 self.sq_stats.taskSkipped()
2493 self.sq_stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002494 self.scenequeue_notcovered.add(task)
2495 self.scenequeue_updatecounters(task, True)
2496
Brad Bishop96ff1982019-08-19 13:50:42 -04002497 def sq_task_skip(self, task):
2498 self.sq_running.add(task)
2499 self.sq_buildable.add(task)
2500 self.sq_task_completeoutright(task)
2501 self.sq_stats.taskSkipped()
2502 self.sq_stats.taskCompleted()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002503
Brad Bishop96ff1982019-08-19 13:50:42 -04002504 def sq_build_taskdepdata(self, task):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002505 def getsetscenedeps(tid):
2506 deps = set()
2507 (mc, fn, taskname, _) = split_tid_mcfn(tid)
2508 realtid = tid + "_setscene"
2509 idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
2510 for (depname, idependtask) in idepends:
2511 if depname not in self.rqdata.taskData[mc].build_targets:
2512 continue
2513
2514 depfn = self.rqdata.taskData[mc].build_targets[depname][0]
2515 if depfn is None:
2516 continue
2517 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2518 deps.add(deptid)
2519 return deps
2520
2521 taskdepdata = {}
2522 next = getsetscenedeps(task)
2523 next.add(task)
2524 while next:
2525 additional = []
2526 for revdep in next:
2527 (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
2528 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2529 deps = getsetscenedeps(revdep)
2530 provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
2531 taskhash = self.rqdata.runtaskentries[revdep].hash
Brad Bishop19323692019-04-05 15:28:33 -04002532 unihash = self.rqdata.runtaskentries[revdep].unihash
2533 taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash, unihash]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002534 for revdep2 in deps:
2535 if revdep2 not in taskdepdata:
2536 additional.append(revdep2)
2537 next = additional
2538
2539 #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
2540 return taskdepdata
2541
Brad Bishop96ff1982019-08-19 13:50:42 -04002542 def check_setscenewhitelist(self, tid):
2543 # Check task that is going to run against the whitelist
2544 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2545 # Ignore covered tasks
2546 if tid in self.tasks_covered:
2547 return False
2548 # Ignore stamped tasks
2549 if self.rq.check_stamp_task(tid, taskname, cache=self.stampcache):
2550 return False
2551 # Ignore noexec tasks
2552 taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
2553 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2554 return False
2555
2556 pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
2557 if not check_setscene_enforce_whitelist(pn, taskname, self.rqdata.setscenewhitelist):
2558 if tid in self.rqdata.runq_setscene_tids:
2559 msg = 'Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)
2560 else:
2561 msg = 'Task %s.%s attempted to execute unexpectedly' % (pn, taskname)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002562 for t in self.scenequeue_notcovered:
2563 msg = msg + "\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash)
Brad Bishop96ff1982019-08-19 13:50:42 -04002564 logger.error(msg + '\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered))
2565 return True
2566 return False
2567
2568class SQData(object):
2569 def __init__(self):
2570 # SceneQueue dependencies
2571 self.sq_deps = {}
2572 # SceneQueue reverse dependencies
2573 self.sq_revdeps = {}
Brad Bishop96ff1982019-08-19 13:50:42 -04002574 # Injected inter-setscene task dependencies
2575 self.sq_harddeps = {}
2576 # Cache of stamp files so duplicates can't run in parallel
2577 self.stamps = {}
2578 # Setscene tasks directly depended upon by the build
2579 self.unskippable = set()
2580 # List of setscene tasks which aren't present
2581 self.outrightfail = set()
2582 # A list of normal tasks a setscene task covers
2583 self.sq_covered_tasks = {}
2584
2585def build_scenequeue_data(sqdata, rqdata, rq, cooker, stampcache, sqrq):
2586
2587 sq_revdeps = {}
2588 sq_revdeps_squash = {}
2589 sq_collated_deps = {}
2590
2591 # We need to construct a dependency graph for the setscene functions. Intermediate
2592 # dependencies between the setscene tasks only complicate the code. This code
2593 # therefore aims to collapse the huge runqueue dependency tree into a smaller one
2594 # only containing the setscene functions.
2595
2596 rqdata.init_progress_reporter.next_stage()
2597
2598 # First process the chains up to the first setscene task.
2599 endpoints = {}
2600 for tid in rqdata.runtaskentries:
2601 sq_revdeps[tid] = copy.copy(rqdata.runtaskentries[tid].revdeps)
2602 sq_revdeps_squash[tid] = set()
2603 if (len(sq_revdeps[tid]) == 0) and tid not in rqdata.runq_setscene_tids:
2604 #bb.warn("Added endpoint %s" % (tid))
2605 endpoints[tid] = set()
2606
2607 rqdata.init_progress_reporter.next_stage()
2608
2609 # Secondly process the chains between setscene tasks.
2610 for tid in rqdata.runq_setscene_tids:
2611 sq_collated_deps[tid] = set()
2612 #bb.warn("Added endpoint 2 %s" % (tid))
2613 for dep in rqdata.runtaskentries[tid].depends:
2614 if tid in sq_revdeps[dep]:
2615 sq_revdeps[dep].remove(tid)
2616 if dep not in endpoints:
2617 endpoints[dep] = set()
2618 #bb.warn(" Added endpoint 3 %s" % (dep))
2619 endpoints[dep].add(tid)
2620
2621 rqdata.init_progress_reporter.next_stage()
2622
2623 def process_endpoints(endpoints):
2624 newendpoints = {}
2625 for point, task in endpoints.items():
2626 tasks = set()
2627 if task:
2628 tasks |= task
2629 if sq_revdeps_squash[point]:
2630 tasks |= sq_revdeps_squash[point]
2631 if point not in rqdata.runq_setscene_tids:
2632 for t in tasks:
2633 sq_collated_deps[t].add(point)
2634 sq_revdeps_squash[point] = set()
2635 if point in rqdata.runq_setscene_tids:
2636 sq_revdeps_squash[point] = tasks
2637 tasks = set()
2638 continue
2639 for dep in rqdata.runtaskentries[point].depends:
2640 if point in sq_revdeps[dep]:
2641 sq_revdeps[dep].remove(point)
2642 if tasks:
2643 sq_revdeps_squash[dep] |= tasks
2644 if len(sq_revdeps[dep]) == 0 and dep not in rqdata.runq_setscene_tids:
2645 newendpoints[dep] = task
2646 if len(newendpoints) != 0:
2647 process_endpoints(newendpoints)
2648
2649 process_endpoints(endpoints)
2650
2651 rqdata.init_progress_reporter.next_stage()
2652
Brad Bishop08902b02019-08-20 09:16:51 -04002653 # Build a list of tasks which are "unskippable"
2654 # These are direct endpoints referenced by the build upto and including setscene tasks
Brad Bishop96ff1982019-08-19 13:50:42 -04002655 # Take the build endpoints (no revdeps) and find the sstate tasks they depend upon
2656 new = True
2657 for tid in rqdata.runtaskentries:
2658 if len(rqdata.runtaskentries[tid].revdeps) == 0:
2659 sqdata.unskippable.add(tid)
Brad Bishop08902b02019-08-20 09:16:51 -04002660 sqdata.unskippable |= sqrq.cantskip
Brad Bishop96ff1982019-08-19 13:50:42 -04002661 while new:
2662 new = False
Brad Bishop08902b02019-08-20 09:16:51 -04002663 orig = sqdata.unskippable.copy()
2664 for tid in sorted(orig, reverse=True):
Brad Bishop96ff1982019-08-19 13:50:42 -04002665 if tid in rqdata.runq_setscene_tids:
2666 continue
Brad Bishop96ff1982019-08-19 13:50:42 -04002667 if len(rqdata.runtaskentries[tid].depends) == 0:
2668 # These are tasks which have no setscene tasks in their chain, need to mark as directly buildable
Brad Bishop96ff1982019-08-19 13:50:42 -04002669 sqrq.setbuildable(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002670 sqdata.unskippable |= rqdata.runtaskentries[tid].depends
Brad Bishop08902b02019-08-20 09:16:51 -04002671 if sqdata.unskippable != orig:
2672 new = True
2673
2674 sqrq.tasks_scenequeue_done |= sqdata.unskippable.difference(rqdata.runq_setscene_tids)
Brad Bishop96ff1982019-08-19 13:50:42 -04002675
2676 rqdata.init_progress_reporter.next_stage(len(rqdata.runtaskentries))
2677
2678 # Sanity check all dependencies could be changed to setscene task references
2679 for taskcounter, tid in enumerate(rqdata.runtaskentries):
2680 if tid in rqdata.runq_setscene_tids:
2681 pass
2682 elif len(sq_revdeps_squash[tid]) != 0:
2683 bb.msg.fatal("RunQueue", "Something went badly wrong during scenequeue generation, aborting. Please report this problem.")
2684 else:
2685 del sq_revdeps_squash[tid]
2686 rqdata.init_progress_reporter.update(taskcounter)
2687
2688 rqdata.init_progress_reporter.next_stage()
2689
2690 # Resolve setscene inter-task dependencies
2691 # e.g. do_sometask_setscene[depends] = "targetname:do_someothertask_setscene"
2692 # Note that anything explicitly depended upon will have its reverse dependencies removed to avoid circular dependencies
2693 for tid in rqdata.runq_setscene_tids:
2694 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2695 realtid = tid + "_setscene"
2696 idepends = rqdata.taskData[mc].taskentries[realtid].idepends
2697 sqdata.stamps[tid] = bb.build.stampfile(taskname + "_setscene", rqdata.dataCaches[mc], taskfn, noextra=True)
2698 for (depname, idependtask) in idepends:
2699
2700 if depname not in rqdata.taskData[mc].build_targets:
2701 continue
2702
2703 depfn = rqdata.taskData[mc].build_targets[depname][0]
2704 if depfn is None:
2705 continue
2706 deptid = depfn + ":" + idependtask.replace("_setscene", "")
2707 if deptid not in rqdata.runtaskentries:
2708 bb.msg.fatal("RunQueue", "Task %s depends upon non-existent task %s:%s" % (realtid, depfn, idependtask))
2709
2710 if not deptid in sqdata.sq_harddeps:
2711 sqdata.sq_harddeps[deptid] = set()
2712 sqdata.sq_harddeps[deptid].add(tid)
2713
2714 sq_revdeps_squash[tid].add(deptid)
2715 # Have to zero this to avoid circular dependencies
2716 sq_revdeps_squash[deptid] = set()
2717
2718 rqdata.init_progress_reporter.next_stage()
2719
2720 for task in sqdata.sq_harddeps:
2721 for dep in sqdata.sq_harddeps[task]:
2722 sq_revdeps_squash[dep].add(task)
2723
2724 rqdata.init_progress_reporter.next_stage()
2725
2726 #for tid in sq_revdeps_squash:
2727 # data = ""
2728 # for dep in sq_revdeps_squash[tid]:
2729 # data = data + "\n %s" % dep
2730 # bb.warn("Task %s_setscene: is %s " % (tid, data))
2731
2732 sqdata.sq_revdeps = sq_revdeps_squash
Brad Bishop96ff1982019-08-19 13:50:42 -04002733 sqdata.sq_covered_tasks = sq_collated_deps
2734
2735 # Build reverse version of revdeps to populate deps structure
2736 for tid in sqdata.sq_revdeps:
2737 sqdata.sq_deps[tid] = set()
2738 for tid in sqdata.sq_revdeps:
2739 for dep in sqdata.sq_revdeps[tid]:
2740 sqdata.sq_deps[dep].add(tid)
2741
2742 rqdata.init_progress_reporter.next_stage()
2743
Brad Bishop00e122a2019-10-05 11:10:57 -04002744 sqdata.multiconfigs = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002745 for tid in sqdata.sq_revdeps:
Brad Bishop00e122a2019-10-05 11:10:57 -04002746 sqdata.multiconfigs.add(mc_from_tid(tid))
Brad Bishop96ff1982019-08-19 13:50:42 -04002747 if len(sqdata.sq_revdeps[tid]) == 0:
2748 sqrq.sq_buildable.add(tid)
2749
2750 rqdata.init_progress_reporter.finish()
2751
Brad Bishop00e122a2019-10-05 11:10:57 -04002752 sqdata.noexec = set()
2753 sqdata.stamppresent = set()
2754 sqdata.valid = set()
Brad Bishop96ff1982019-08-19 13:50:42 -04002755
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002756 update_scenequeue_data(sqdata.sq_revdeps, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True)
Brad Bishop00e122a2019-10-05 11:10:57 -04002757
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002758def update_scenequeue_data(tids, sqdata, rqdata, rq, cooker, stampcache, sqrq, summary=True):
Brad Bishop00e122a2019-10-05 11:10:57 -04002759
2760 tocheck = set()
2761
2762 for tid in sorted(tids):
2763 if tid in sqdata.stamppresent:
2764 sqdata.stamppresent.remove(tid)
2765 if tid in sqdata.valid:
2766 sqdata.valid.remove(tid)
2767
2768 (mc, fn, taskname, taskfn) = split_tid_mcfn(tid)
2769
2770 taskdep = rqdata.dataCaches[mc].task_deps[taskfn]
2771
2772 if 'noexec' in taskdep and taskname in taskdep['noexec']:
2773 sqdata.noexec.add(tid)
2774 sqrq.sq_task_skip(tid)
2775 bb.build.make_stamp(taskname + "_setscene", rqdata.dataCaches[mc], taskfn)
2776 continue
2777
2778 if rq.check_stamp_task(tid, taskname + "_setscene", cache=stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002779 logger.debug2('Setscene stamp current for task %s', tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04002780 sqdata.stamppresent.add(tid)
2781 sqrq.sq_task_skip(tid)
2782 continue
2783
2784 if rq.check_stamp_task(tid, taskname, recurse = True, cache=stampcache):
Andrew Geisslerd1e89492021-02-12 15:35:20 -06002785 logger.debug2('Normal stamp current for task %s', tid)
Brad Bishop00e122a2019-10-05 11:10:57 -04002786 sqdata.stamppresent.add(tid)
2787 sqrq.sq_task_skip(tid)
2788 continue
2789
2790 tocheck.add(tid)
2791
Brad Bishop1d80a2e2019-11-15 16:35:03 -05002792 sqdata.valid |= rq.validate_hashes(tocheck, cooker.data, len(sqdata.stamppresent), False, summary=summary)
Brad Bishop00e122a2019-10-05 11:10:57 -04002793
2794 sqdata.hashes = {}
2795 for mc in sorted(sqdata.multiconfigs):
Brad Bishop08902b02019-08-20 09:16:51 -04002796 for tid in sorted(sqdata.sq_revdeps):
Brad Bishop96ff1982019-08-19 13:50:42 -04002797 if mc_from_tid(tid) != mc:
2798 continue
Brad Bishop00e122a2019-10-05 11:10:57 -04002799 if tid in sqdata.stamppresent:
2800 continue
2801 if tid in sqdata.valid:
2802 continue
2803 if tid in sqdata.noexec:
2804 continue
2805 if tid in sqrq.scenequeue_notcovered:
2806 continue
2807 sqdata.outrightfail.add(tid)
Brad Bishop96ff1982019-08-19 13:50:42 -04002808
Brad Bishop00e122a2019-10-05 11:10:57 -04002809 h = pending_hash_index(tid, rqdata)
2810 if h not in sqdata.hashes:
2811 sqdata.hashes[h] = tid
2812 else:
2813 sqrq.sq_deferred[tid] = sqdata.hashes[h]
Andrew Geissler82c905d2020-04-13 13:39:40 -05002814 bb.note("Deferring %s after %s" % (tid, sqdata.hashes[h]))
Brad Bishop96ff1982019-08-19 13:50:42 -04002815
2816
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002817class TaskFailure(Exception):
2818 """
2819 Exception raised when a task in a runqueue fails
2820 """
2821 def __init__(self, x):
2822 self.args = x
2823
2824
2825class runQueueExitWait(bb.event.Event):
2826 """
2827 Event when waiting for task processes to exit
2828 """
2829
2830 def __init__(self, remain):
2831 self.remain = remain
2832 self.message = "Waiting for %s active tasks to finish" % remain
2833 bb.event.Event.__init__(self)
2834
2835class runQueueEvent(bb.event.Event):
2836 """
2837 Base runQueue event class
2838 """
2839 def __init__(self, task, stats, rq):
2840 self.taskid = task
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002841 self.taskstring = task
2842 self.taskname = taskname_from_tid(task)
2843 self.taskfile = fn_from_tid(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002844 self.taskhash = rq.rqdata.get_task_hash(task)
2845 self.stats = stats.copy()
2846 bb.event.Event.__init__(self)
2847
2848class sceneQueueEvent(runQueueEvent):
2849 """
2850 Base sceneQueue event class
2851 """
2852 def __init__(self, task, stats, rq, noexec=False):
2853 runQueueEvent.__init__(self, task, stats, rq)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002854 self.taskstring = task + "_setscene"
2855 self.taskname = taskname_from_tid(task) + "_setscene"
2856 self.taskfile = fn_from_tid(task)
2857 self.taskhash = rq.rqdata.get_task_hash(task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002858
2859class runQueueTaskStarted(runQueueEvent):
2860 """
2861 Event notifying a task was started
2862 """
2863 def __init__(self, task, stats, rq, noexec=False):
2864 runQueueEvent.__init__(self, task, stats, rq)
2865 self.noexec = noexec
2866
2867class sceneQueueTaskStarted(sceneQueueEvent):
2868 """
2869 Event notifying a setscene task was started
2870 """
2871 def __init__(self, task, stats, rq, noexec=False):
2872 sceneQueueEvent.__init__(self, task, stats, rq)
2873 self.noexec = noexec
2874
2875class runQueueTaskFailed(runQueueEvent):
2876 """
2877 Event notifying a task failed
2878 """
2879 def __init__(self, task, stats, exitcode, rq):
2880 runQueueEvent.__init__(self, task, stats, rq)
2881 self.exitcode = exitcode
2882
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002883 def __str__(self):
2884 return "Task (%s) failed with exit code '%s'" % (self.taskstring, self.exitcode)
2885
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002886class sceneQueueTaskFailed(sceneQueueEvent):
2887 """
2888 Event notifying a setscene task failed
2889 """
2890 def __init__(self, task, stats, exitcode, rq):
2891 sceneQueueEvent.__init__(self, task, stats, rq)
2892 self.exitcode = exitcode
2893
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002894 def __str__(self):
2895 return "Setscene task (%s) failed with exit code '%s' - real task will be run instead" % (self.taskstring, self.exitcode)
2896
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002897class sceneQueueComplete(sceneQueueEvent):
2898 """
2899 Event when all the sceneQueue tasks are complete
2900 """
2901 def __init__(self, stats, rq):
2902 self.stats = stats.copy()
2903 bb.event.Event.__init__(self)
2904
2905class runQueueTaskCompleted(runQueueEvent):
2906 """
2907 Event notifying a task completed
2908 """
2909
2910class sceneQueueTaskCompleted(sceneQueueEvent):
2911 """
2912 Event notifying a setscene task completed
2913 """
2914
2915class runQueueTaskSkipped(runQueueEvent):
2916 """
2917 Event notifying a task was skipped
2918 """
2919 def __init__(self, task, stats, rq, reason):
2920 runQueueEvent.__init__(self, task, stats, rq)
2921 self.reason = reason
2922
Brad Bishop08902b02019-08-20 09:16:51 -04002923class taskUniHashUpdate(bb.event.Event):
2924 """
2925 Base runQueue event class
2926 """
2927 def __init__(self, task, unihash):
2928 self.taskid = task
2929 self.unihash = unihash
2930 bb.event.Event.__init__(self)
2931
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002932class runQueuePipe():
2933 """
2934 Abstraction for a pipe between a worker thread and the server
2935 """
2936 def __init__(self, pipein, pipeout, d, rq, rqexec):
2937 self.input = pipein
2938 if pipeout:
2939 pipeout.close()
2940 bb.utils.nonblockingfd(self.input)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002941 self.queue = b""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002942 self.d = d
2943 self.rq = rq
2944 self.rqexec = rqexec
2945
2946 def setrunqueueexec(self, rqexec):
2947 self.rqexec = rqexec
2948
2949 def read(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002950 for workers, name in [(self.rq.worker, "Worker"), (self.rq.fakeworker, "Fakeroot")]:
2951 for worker in workers.values():
2952 worker.process.poll()
2953 if worker.process.returncode is not None and not self.rq.teardown:
2954 bb.error("%s process (%s) exited unexpectedly (%s), shutting down..." % (name, worker.process.pid, str(worker.process.returncode)))
2955 self.rq.finish_runqueue(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002956
2957 start = len(self.queue)
2958 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002959 self.queue = self.queue + (self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002960 except (OSError, IOError) as e:
2961 if e.errno != errno.EAGAIN:
2962 raise
2963 end = len(self.queue)
2964 found = True
2965 while found and len(self.queue):
2966 found = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002967 index = self.queue.find(b"</event>")
2968 while index != -1 and self.queue.startswith(b"<event>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002969 try:
2970 event = pickle.loads(self.queue[7:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05002971 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
2972 if isinstance(e, pickle.UnpicklingError) and "truncated" in str(e):
2973 # The pickled data could contain "</event>" so search for the next occurance
2974 # unpickling again, this should be the only way an unpickle error could occur
2975 index = self.queue.find(b"</event>", index + 1)
2976 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002977 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[7:index]))
2978 bb.event.fire_from_worker(event, self.d)
Brad Bishop08902b02019-08-20 09:16:51 -04002979 if isinstance(event, taskUniHashUpdate):
2980 self.rqexec.updated_taskhash_queue.append((event.taskid, event.unihash))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002981 found = True
2982 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002983 index = self.queue.find(b"</event>")
2984 index = self.queue.find(b"</exitcode>")
2985 while index != -1 and self.queue.startswith(b"<exitcode>"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002986 try:
2987 task, status = pickle.loads(self.queue[10:index])
Andrew Geissler475cb722020-07-10 16:00:51 -05002988 except (ValueError, pickle.UnpicklingError, AttributeError, IndexError) as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002989 bb.msg.fatal("RunQueue", "failed load pickle '%s': '%s'" % (e, self.queue[10:index]))
2990 self.rqexec.runqueue_process_waitpid(task, status)
2991 found = True
2992 self.queue = self.queue[index+11:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002993 index = self.queue.find(b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002994 return (end > start)
2995
2996 def close(self):
2997 while self.read():
2998 continue
2999 if len(self.queue) > 0:
3000 print("Warning, worker left partial message: %s" % self.queue)
3001 self.input.close()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003002
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003003def get_setscene_enforce_whitelist(d, targets):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003004 if d.getVar('BB_SETSCENE_ENFORCE') != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003005 return None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003006 whitelist = (d.getVar("BB_SETSCENE_ENFORCE_WHITELIST") or "").split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003007 outlist = []
3008 for item in whitelist[:]:
3009 if item.startswith('%:'):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05003010 for (mc, target, task, fn) in targets:
3011 outlist.append(target + ':' + item.split(':')[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003012 else:
3013 outlist.append(item)
3014 return outlist
3015
3016def check_setscene_enforce_whitelist(pn, taskname, whitelist):
3017 import fnmatch
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003018 if whitelist is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06003019 item = '%s:%s' % (pn, taskname)
3020 for whitelist_item in whitelist:
3021 if fnmatch.fnmatch(item, whitelist_item):
3022 return True
3023 return False
3024 return True