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