blob: 1797a1d4ca5f561a864449859e32f82748e7920e [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#
2# Copyright (C) 2003, 2004 Chris Larson
3# Copyright (C) 2003, 2004 Phil Blundell
4# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
5# Copyright (C) 2005 Holger Hans Peter Freyther
6# Copyright (C) 2005 ROAD GmbH
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 Williamsc0f7c042017-02-23 20:41:17 -060011
Patrick Williamsc124f4f2015-09-15 14:41:29 -050012import sys, os, glob, os.path, re, time
Patrick Williamsc124f4f2015-09-15 14:41:29 -050013import itertools
14import logging
15import multiprocessing
Patrick Williamsc124f4f2015-09-15 14:41:29 -050016import threading
Patrick Williamsc0f7c042017-02-23 20:41:17 -060017from io import StringIO, UnsupportedOperation
Patrick Williamsc124f4f2015-09-15 14:41:29 -050018from contextlib import closing
Patrick Williamsc0f7c042017-02-23 20:41:17 -060019from collections import defaultdict, namedtuple
Patrick Williamsc124f4f2015-09-15 14:41:29 -050020import bb, bb.exceptions, bb.command
21from bb import utils, data, parse, event, cache, providers, taskdata, runqueue, build
Patrick Williamsc0f7c042017-02-23 20:41:17 -060022import queue
Patrick Williamsc124f4f2015-09-15 14:41:29 -050023import signal
Patrick Williamsc124f4f2015-09-15 14:41:29 -050024import prserv.serv
25import pyinotify
Patrick Williamsc0f7c042017-02-23 20:41:17 -060026import json
27import pickle
28import codecs
Brad Bishop08902b02019-08-20 09:16:51 -040029import hashserv
Patrick Williamsc124f4f2015-09-15 14:41:29 -050030
31logger = logging.getLogger("BitBake")
32collectlog = logging.getLogger("BitBake.Collection")
33buildlog = logging.getLogger("BitBake.Build")
34parselog = logging.getLogger("BitBake.Parsing")
35providerlog = logging.getLogger("BitBake.Provider")
36
37class NoSpecificMatch(bb.BBHandledException):
38 """
39 Exception raised when no or multiple file matches are found
40 """
41
42class NothingToBuild(Exception):
43 """
44 Exception raised when there is nothing to build
45 """
46
47class CollectionError(bb.BBHandledException):
48 """
49 Exception raised when layer configuration is incorrect
50 """
51
52class state:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060053 initial, parsing, running, shutdown, forceshutdown, stopped, error = list(range(7))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050054
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050055 @classmethod
56 def get_name(cls, code):
57 for name in dir(cls):
58 value = getattr(cls, name)
59 if type(value) == type(cls.initial) and value == code:
60 return name
61 raise ValueError("Invalid status code: %s" % code)
62
Patrick Williamsc124f4f2015-09-15 14:41:29 -050063
64class SkippedPackage:
65 def __init__(self, info = None, reason = None):
66 self.pn = None
67 self.skipreason = None
68 self.provides = None
69 self.rprovides = None
70
71 if info:
72 self.pn = info.pn
73 self.skipreason = info.skipreason
74 self.provides = info.provides
Andrew Geisslerd1e89492021-02-12 15:35:20 -060075 self.rprovides = info.packages + info.rprovides
76 for package in info.packages:
77 self.rprovides += info.rprovides_pkg[package]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050078 elif reason:
79 self.skipreason = reason
80
81
82class CookerFeatures(object):
Andrew Geissler517393d2023-01-13 08:55:19 -060083 _feature_list = [HOB_EXTRA_CACHES, BASEDATASTORE_TRACKING, SEND_SANITYEVENTS, RECIPE_SIGGEN_INFO] = list(range(4))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050084
85 def __init__(self):
86 self._features=set()
87
88 def setFeature(self, f):
89 # validate we got a request for a feature we support
90 if f not in CookerFeatures._feature_list:
91 return
92 self._features.add(f)
93
94 def __contains__(self, f):
95 return f in self._features
96
97 def __iter__(self):
98 return self._features.__iter__()
99
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600100 def __next__(self):
101 return next(self._features)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102
103
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600104class EventWriter:
105 def __init__(self, cooker, eventfile):
106 self.file_inited = None
107 self.cooker = cooker
108 self.eventfile = eventfile
109 self.event_queue = []
110
111 def write_event(self, event):
112 with open(self.eventfile, "a") as f:
113 try:
114 str_event = codecs.encode(pickle.dumps(event), 'base64').decode('utf-8')
115 f.write("%s\n" % json.dumps({"class": event.__module__ + "." + event.__class__.__name__,
116 "vars": str_event}))
117 except Exception as err:
118 import traceback
119 print(err, traceback.format_exc())
120
121 def send(self, event):
122 if self.file_inited:
123 # we have the file, just write the event
124 self.write_event(event)
125 else:
126 # init on bb.event.BuildStarted
127 name = "%s.%s" % (event.__module__, event.__class__.__name__)
128 if name in ("bb.event.BuildStarted", "bb.cooker.CookerExit"):
129 with open(self.eventfile, "w") as f:
130 f.write("%s\n" % json.dumps({ "allvariables" : self.cooker.getAllKeysWithFlags(["doc", "func"])}))
131
132 self.file_inited = True
133
134 # write pending events
135 for evt in self.event_queue:
136 self.write_event(evt)
137
138 # also write the current event
139 self.write_event(event)
140 else:
141 # queue all events until the file is inited
142 self.event_queue.append(event)
143
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500144#============================================================================#
145# BBCooker
146#============================================================================#
147class BBCooker:
148 """
149 Manages one bitbake build run
150 """
151
Andrew Geissler517393d2023-01-13 08:55:19 -0600152 def __init__(self, featureSet=None, server=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600153 self.recipecaches = None
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500154 self.eventlog = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500155 self.skiplist = {}
156 self.featureset = CookerFeatures()
157 if featureSet:
158 for f in featureSet:
159 self.featureset.setFeature(f)
160
Patrick Williams45852732022-04-02 08:58:32 -0500161 self.orig_syspath = sys.path.copy()
162 self.orig_sysmodules = [*sys.modules]
163
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500164 self.configuration = bb.cookerdata.CookerConfiguration()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500165
Andrew Geissler517393d2023-01-13 08:55:19 -0600166 self.process_server = server
167 self.idleCallBackRegister = None
168 self.waitIdle = None
169 if server:
170 self.idleCallBackRegister = server.register_idle_function
171 self.waitIdle = server.wait_for_idle
Andrew Geissler635e0e42020-08-21 15:58:33 -0500172
Brad Bishopf058f492019-01-28 23:50:33 -0500173 bb.debug(1, "BBCooker starting %s" % time.time())
174 sys.stdout.flush()
175
Patrick Williamsde0582f2022-04-08 10:23:27 -0500176 self.configwatcher = None
177 self.confignotifier = None
Brad Bishopf058f492019-01-28 23:50:33 -0500178
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500179 self.watchmask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CREATE | pyinotify.IN_DELETE | \
180 pyinotify.IN_DELETE_SELF | pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF | \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500181 pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500182
Patrick Williamsde0582f2022-04-08 10:23:27 -0500183 self.watcher = None
184 self.notifier = None
Brad Bishopf058f492019-01-28 23:50:33 -0500185
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500186 # If being called by something like tinfoil, we need to clean cached data
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500187 # which may now be invalid
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500188 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500189 bb.parse.BBHandler.cached_statements = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500190
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500191 self.ui_cmdline = None
Brad Bishop08902b02019-08-20 09:16:51 -0400192 self.hashserv = None
Brad Bishopa34c0302019-09-23 22:34:48 -0400193 self.hashservaddr = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500194
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195 self.inotify_modified_files = []
196
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500197 # TOSTOP must not be set or our children will hang when they output
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600198 try:
199 fd = sys.stdout.fileno()
200 if os.isatty(fd):
201 import termios
202 tcattr = termios.tcgetattr(fd)
203 if tcattr[3] & termios.TOSTOP:
204 buildlog.info("The terminal had the TOSTOP bit set, clearing...")
205 tcattr[3] = tcattr[3] & ~termios.TOSTOP
206 termios.tcsetattr(fd, termios.TCSANOW, tcattr)
207 except UnsupportedOperation:
208 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500209
Andrew Geissler517393d2023-01-13 08:55:19 -0600210 self.command = bb.command.Command(self, self.process_server)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500211 self.state = state.initial
212
213 self.parser = None
214
215 signal.signal(signal.SIGTERM, self.sigterm_exception)
216 # Let SIGHUP exit as SIGTERM
217 signal.signal(signal.SIGHUP, self.sigterm_exception)
218
Brad Bishopf058f492019-01-28 23:50:33 -0500219 bb.debug(1, "BBCooker startup complete %s" % time.time())
220 sys.stdout.flush()
221
Andrew Geissler517393d2023-01-13 08:55:19 -0600222 self.inotify_threadlock = threading.Lock()
223
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500224 def init_configdata(self):
225 if not hasattr(self, "data"):
226 self.initConfigurationData()
227 bb.debug(1, "BBCooker parsed base configuration %s" % time.time())
228 sys.stdout.flush()
229 self.handlePRServ()
230
Patrick Williamsde0582f2022-04-08 10:23:27 -0500231 def setupConfigWatcher(self):
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600232 with bb.utils.lock_timeout(self.inotify_threadlock):
233 if self.configwatcher:
234 self.configwatcher.close()
235 self.confignotifier = None
236 self.configwatcher = None
237 self.configwatcher = pyinotify.WatchManager()
238 self.configwatcher.bbseen = set()
239 self.configwatcher.bbwatchedfiles = set()
240 self.confignotifier = pyinotify.Notifier(self.configwatcher, self.config_notifications)
Patrick Williamsde0582f2022-04-08 10:23:27 -0500241
242 def setupParserWatcher(self):
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600243 with bb.utils.lock_timeout(self.inotify_threadlock):
244 if self.watcher:
245 self.watcher.close()
246 self.notifier = None
247 self.watcher = None
248 self.watcher = pyinotify.WatchManager()
249 self.watcher.bbseen = set()
250 self.watcher.bbwatchedfiles = set()
251 self.notifier = pyinotify.Notifier(self.watcher, self.notifications)
Patrick Williamsde0582f2022-04-08 10:23:27 -0500252
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500253 def process_inotify_updates(self):
Andrew Geissler517393d2023-01-13 08:55:19 -0600254 with bb.utils.lock_timeout(self.inotify_threadlock):
255 for n in [self.confignotifier, self.notifier]:
256 if n and n.check_events(timeout=0):
257 # read notified events and enqueue them
258 n.read_events()
259
260 def process_inotify_updates_apply(self):
261 with bb.utils.lock_timeout(self.inotify_threadlock):
262 for n in [self.confignotifier, self.notifier]:
263 if n and n.check_events(timeout=0):
264 n.read_events()
265 n.process_events()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500266
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500267 def config_notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500268 if event.maskname == "IN_Q_OVERFLOW":
269 bb.warn("inotify event queue overflowed, invalidating caches.")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500270 self.parsecache_valid = False
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500271 self.baseconfig_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500272 bb.parse.clear_cache()
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500273 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274 if not event.pathname in self.configwatcher.bbwatchedfiles:
275 return
Andrew Geissler9aee5002022-03-30 16:27:02 +0000276 if "IN_ISDIR" in event.maskname:
Patrick Williams45852732022-04-02 08:58:32 -0500277 if "IN_CREATE" in event.maskname or "IN_DELETE" in event.maskname:
278 if event.pathname in self.configwatcher.bbseen:
279 self.configwatcher.bbseen.remove(event.pathname)
280 # Could remove all entries starting with the directory but for now...
281 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500282 if not event.pathname in self.inotify_modified_files:
283 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500284 self.baseconfig_valid = False
285
286 def notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500287 if event.maskname == "IN_Q_OVERFLOW":
288 bb.warn("inotify event queue overflowed, invalidating caches.")
289 self.parsecache_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500290 bb.parse.clear_cache()
291 return
292 if event.pathname.endswith("bitbake-cookerdaemon.log") \
293 or event.pathname.endswith("bitbake.lock"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500294 return
Andrew Geissler9aee5002022-03-30 16:27:02 +0000295 if "IN_ISDIR" in event.maskname:
Patrick Williams45852732022-04-02 08:58:32 -0500296 if "IN_CREATE" in event.maskname or "IN_DELETE" in event.maskname:
297 if event.pathname in self.watcher.bbseen:
298 self.watcher.bbseen.remove(event.pathname)
299 # Could remove all entries starting with the directory but for now...
300 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500301 if not event.pathname in self.inotify_modified_files:
302 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500303 self.parsecache_valid = False
304
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500305 def add_filewatch(self, deps, watcher=None, dirs=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500306 if not watcher:
307 watcher = self.watcher
308 for i in deps:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500309 watcher.bbwatchedfiles.add(i[0])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500310 if dirs:
311 f = i[0]
312 else:
313 f = os.path.dirname(i[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500314 if f in watcher.bbseen:
315 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -0500316 watcher.bbseen.add(f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317 watchtarget = None
318 while True:
319 # We try and add watches for files that don't exist but if they did, would influence
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500320 # the parser. The parent directory of these files may not exist, in which case we need
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321 # to watch any parent that does exist for changes.
322 try:
323 watcher.add_watch(f, self.watchmask, quiet=False)
324 if watchtarget:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500325 watcher.bbwatchedfiles.add(watchtarget)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500326 break
327 except pyinotify.WatchManagerError as e:
328 if 'ENOENT' in str(e):
329 watchtarget = f
330 f = os.path.dirname(f)
331 if f in watcher.bbseen:
332 break
Andrew Geissler82c905d2020-04-13 13:39:40 -0500333 watcher.bbseen.add(f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500334 continue
335 if 'ENOSPC' in str(e):
336 providerlog.error("No space left on device or exceeds fs.inotify.max_user_watches?")
337 providerlog.error("To check max_user_watches: sysctl -n fs.inotify.max_user_watches.")
338 providerlog.error("To modify max_user_watches: sysctl -n -w fs.inotify.max_user_watches=<value>.")
339 providerlog.error("Root privilege is required to modify max_user_watches.")
340 raise
341
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600342 def handle_inotify_updates(self):
343 # reload files for which we got notifications
344 for p in self.inotify_modified_files:
345 bb.parse.update_cache(p)
346 if p in bb.parse.BBHandler.cached_statements:
347 del bb.parse.BBHandler.cached_statements[p]
348 self.inotify_modified_files = []
349
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500350 def sigterm_exception(self, signum, stackframe):
351 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500352 bb.warn("Cooker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500353 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500354 bb.warn("Cooker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500355 self.state = state.forceshutdown
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600356 bb.event._should_exit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500357
358 def setFeatures(self, features):
359 # we only accept a new feature set if we're in state initial, so we can reset without problems
360 if not self.state in [state.initial, state.shutdown, state.forceshutdown, state.stopped, state.error]:
361 raise Exception("Illegal state for feature set change")
362 original_featureset = list(self.featureset)
363 for feature in features:
364 self.featureset.setFeature(feature)
365 bb.debug(1, "Features set %s (was %s)" % (original_featureset, list(self.featureset)))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500366 if (original_featureset != list(self.featureset)) and self.state != state.error and hasattr(self, "data"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500367 self.reset()
368
369 def initConfigurationData(self):
370
371 self.state = state.initial
372 self.caches_array = []
373
Patrick Williams45852732022-04-02 08:58:32 -0500374 sys.path = self.orig_syspath.copy()
375 for mod in [*sys.modules]:
376 if mod not in self.orig_sysmodules:
377 del sys.modules[mod]
378
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600379 self.handle_inotify_updates()
Patrick Williamsde0582f2022-04-08 10:23:27 -0500380 self.setupConfigWatcher()
381
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500382 # Need to preserve BB_CONSOLELOG over resets
383 consolelog = None
384 if hasattr(self, "data"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500385 consolelog = self.data.getVar("BB_CONSOLELOG")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500386
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500387 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
388 self.enableDataTracking()
389
Andrew Geissler517393d2023-01-13 08:55:19 -0600390 caches_name_array = ['bb.cache:CoreRecipeInfo']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500391 # We hardcode all known cache types in a single place, here.
392 if CookerFeatures.HOB_EXTRA_CACHES in self.featureset:
Andrew Geissler517393d2023-01-13 08:55:19 -0600393 caches_name_array.append("bb.cache_extra:HobRecipeInfo")
394 if CookerFeatures.RECIPE_SIGGEN_INFO in self.featureset:
395 caches_name_array.append("bb.cache:SiggenRecipeInfo")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500396
397 # At least CoreRecipeInfo will be loaded, so caches_array will never be empty!
398 # This is the entry point, no further check needed!
399 for var in caches_name_array:
400 try:
401 module_name, cache_name = var.split(':')
402 module = __import__(module_name, fromlist=(cache_name,))
403 self.caches_array.append(getattr(module, cache_name))
404 except ImportError as exc:
405 logger.critical("Unable to import extra RecipeInfo '%s' from '%s': %s" % (cache_name, module_name, exc))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500406 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500407
408 self.databuilder = bb.cookerdata.CookerDataBuilder(self.configuration, False)
409 self.databuilder.parseBaseConfiguration()
410 self.data = self.databuilder.data
411 self.data_hash = self.databuilder.data_hash
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500412 self.extraconfigdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500413
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500414 if consolelog:
415 self.data.setVar("BB_CONSOLELOG", consolelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500416
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500417 self.data.setVar('BB_CMDLINE', self.ui_cmdline)
418
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500419 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
420 self.disableDataTracking()
421
Brad Bishop15ae2502019-06-18 21:44:24 -0400422 for mc in self.databuilder.mcdata.values():
Brad Bishop15ae2502019-06-18 21:44:24 -0400423 self.add_filewatch(mc.getVar("__base_depends", False), self.configwatcher)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500424
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500425 self.baseconfig_valid = True
426 self.parsecache_valid = False
427
428 def handlePRServ(self):
429 # Setup a PR Server based on the new configuration
430 try:
431 self.prhost = prserv.serv.auto_start(self.data)
432 except prserv.serv.PRServiceConfigError as e:
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500433 bb.fatal("Unable to start PR Server, exiting, check the bitbake-cookerdaemon.log")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500434
Brad Bishopa34c0302019-09-23 22:34:48 -0400435 if self.data.getVar("BB_HASHSERVE") == "auto":
436 # Create a new hash server bound to a unix domain socket
Brad Bishop08902b02019-08-20 09:16:51 -0400437 if not self.hashserv:
438 dbfile = (self.data.getVar("PERSISTENT_DIR") or self.data.getVar("CACHE")) + "/hashserv.db"
Andrew Geissler595f6302022-01-24 19:11:47 +0000439 upstream = self.data.getVar("BB_HASHSERVE_UPSTREAM") or None
440 if upstream:
441 import socket
442 try:
443 sock = socket.create_connection(upstream.split(":"), 5)
444 sock.close()
445 except socket.error as e:
Andrew Geissler87f5cff2022-09-30 13:13:31 -0500446 bb.warn("BB_HASHSERVE_UPSTREAM is not valid, unable to connect hash equivalence server at '%s': %s"
Andrew Geissler595f6302022-01-24 19:11:47 +0000447 % (upstream, repr(e)))
448
Brad Bishopa34c0302019-09-23 22:34:48 -0400449 self.hashservaddr = "unix://%s/hashserve.sock" % self.data.getVar("TOPDIR")
Andrew Geissler5199d832021-09-24 16:47:35 -0500450 self.hashserv = hashserv.create_server(
451 self.hashservaddr,
452 dbfile,
453 sync=False,
Andrew Geissler595f6302022-01-24 19:11:47 +0000454 upstream=upstream,
Andrew Geissler5199d832021-09-24 16:47:35 -0500455 )
Patrick Williams213cb262021-08-07 19:21:33 -0500456 self.hashserv.serve_as_process()
Brad Bishop08902b02019-08-20 09:16:51 -0400457 for mc in self.databuilder.mcdata:
Andrew Geissler517393d2023-01-13 08:55:19 -0600458 self.databuilder.mcorigdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishopa34c0302019-09-23 22:34:48 -0400459 self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400460
461 bb.parse.init_parser(self.data)
462
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500463 def enableDataTracking(self):
464 self.configuration.tracking = True
465 if hasattr(self, "data"):
466 self.data.enableTracking()
467
468 def disableDataTracking(self):
469 self.configuration.tracking = False
470 if hasattr(self, "data"):
471 self.data.disableTracking()
472
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500473 def parseConfiguration(self):
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600474 self.updateCacheSync()
475
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500476 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500477 nice = self.data.getVar("BB_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500478 if nice:
479 curnice = os.nice(0)
480 nice = int(nice) - curnice
481 buildlog.verbose("Renice to %s " % os.nice(nice))
482
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600483 if self.recipecaches:
484 del self.recipecaches
485 self.multiconfigs = self.databuilder.mcdata.keys()
486 self.recipecaches = {}
487 for mc in self.multiconfigs:
488 self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500489
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500490 self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500492 self.parsecache_valid = False
493
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500494 def updateConfigOpts(self, options, environment, cmdline):
495 self.ui_cmdline = cmdline
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500496 clean = True
497 for o in options:
498 if o in ['prefile', 'postfile']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500499 # Only these options may require a reparse
500 try:
501 if getattr(self.configuration, o) == options[o]:
502 # Value is the same, no need to mark dirty
503 continue
504 except AttributeError:
505 pass
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600506 logger.debug("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500507 print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500508 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500509 if hasattr(self.configuration, o):
510 setattr(self.configuration, o, options[o])
511
512 if self.configuration.writeeventlog:
513 if self.eventlog and self.eventlog[0] != self.configuration.writeeventlog:
514 bb.event.unregister_UIHhandler(self.eventlog[1])
515 if not self.eventlog or self.eventlog[0] != self.configuration.writeeventlog:
516 # we log all events to a file if so directed
517 # register the log file writer as UI Handler
518 writer = EventWriter(self, self.configuration.writeeventlog)
519 EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
520 self.eventlog = (self.configuration.writeeventlog, bb.event.register_UIHhandler(EventLogWriteHandler(writer)))
521
522 bb.msg.loggerDefaultLogLevel = self.configuration.default_loglevel
523 bb.msg.loggerDefaultDomains = self.configuration.debug_domains
524
525 if hasattr(self, "data"):
526 origenv = bb.data.init()
527 for k in environment:
528 origenv.setVar(k, environment[k])
529 self.data.setVar("BB_ORIGENV", origenv)
530
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500531 for k in bb.utils.approved_variables():
532 if k in environment and k not in self.configuration.env:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600533 logger.debug("Updating new environment variable %s to %s" % (k, environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534 self.configuration.env[k] = environment[k]
535 clean = False
536 if k in self.configuration.env and k not in environment:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600537 logger.debug("Updating environment variable %s (deleted)" % (k))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500538 del self.configuration.env[k]
539 clean = False
540 if k not in self.configuration.env and k not in environment:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500541 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500542 if environment[k] != self.configuration.env[k]:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600543 logger.debug("Updating environment variable %s from %s to %s" % (k, self.configuration.env[k], environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500544 self.configuration.env[k] = environment[k]
545 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500546
547 # Now update all the variables not in the datastore to match
548 self.configuration.env = environment
549
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500550 if not clean:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600551 logger.debug("Base environment change, triggering reparse")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500552 self.reset()
553
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500554 def showVersions(self):
555
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500556 (latest_versions, preferred_versions, required) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500557
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500558 logger.plain("%-35s %25s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version", "Required Version")
559 logger.plain("%-35s %25s %25s %25s\n", "===========", "==============", "=================", "================")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500560
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500561 for p in sorted(self.recipecaches[''].pkg_pn):
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500562 preferred = preferred_versions[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500563 latest = latest_versions[p]
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500564 requiredstr = ""
565 preferredstr = ""
566 if required[p]:
567 if preferred[0] is not None:
568 requiredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
569 else:
570 bb.fatal("REQUIRED_VERSION of package %s not available" % p)
571 else:
572 preferredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500574 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
575
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500576 if preferred == latest:
577 preferredstr = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500578
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500579 logger.plain("%-35s %25s %25s %25s", p, lateststr, preferredstr, requiredstr)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500580
581 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
582 """
583 Show the outer or per-recipe environment
584 """
585 fn = None
586 envdata = None
Brad Bishop15ae2502019-06-18 21:44:24 -0400587 mc = ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500588 if not pkgs_to_build:
589 pkgs_to_build = []
590
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500591 orig_tracking = self.configuration.tracking
592 if not orig_tracking:
593 self.enableDataTracking()
594 self.reset()
Andrew Geissler9aee5002022-03-30 16:27:02 +0000595 # reset() resets to the UI requested value so we have to redo this
596 self.enableDataTracking()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500597
Brad Bishop15ae2502019-06-18 21:44:24 -0400598 def mc_base(p):
599 if p.startswith('mc:'):
600 s = p.split(':')
601 if len(s) == 2:
602 return s[1]
603 return None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500604
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500605 if buildfile:
606 # Parse the configuration here. We need to do it explicitly here since
607 # this showEnvironment() code path doesn't use the cache
608 self.parseConfiguration()
609
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600610 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500611 fn = self.matchFile(fn, mc)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600612 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500613 elif len(pkgs_to_build) == 1:
Brad Bishop15ae2502019-06-18 21:44:24 -0400614 mc = mc_base(pkgs_to_build[0])
615 if not mc:
616 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
617 if pkgs_to_build[0] in set(ignore.split()):
618 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500619
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000620 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.halt, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500621
Brad Bishop15ae2502019-06-18 21:44:24 -0400622 mc = runlist[0][0]
623 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500624
625 if fn:
626 try:
Andrew Geissler517393d2023-01-13 08:55:19 -0600627 envdata = self.databuilder.parseRecipe(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500628 except Exception as e:
629 parselog.exception("Unable to read %s", fn)
630 raise
Brad Bishop15ae2502019-06-18 21:44:24 -0400631 else:
632 if not mc in self.databuilder.mcdata:
633 bb.fatal('Not multiconfig named "%s" found' % mc)
634 envdata = self.databuilder.mcdata[mc]
635 data.expandKeys(envdata)
636 parse.ast.runAnonFuncs(envdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500637
638 # Display history
639 with closing(StringIO()) as env:
640 self.data.inchistory.emit(env)
641 logger.plain(env.getvalue())
642
643 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500644 with closing(StringIO()) as env:
645 data.emit_env(env, envdata, True)
646 logger.plain(env.getvalue())
647
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000648 # emit the metadata which isn't valid shell
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500649 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600650 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500651 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500652
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500653 if not orig_tracking:
654 self.disableDataTracking()
655 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000657 def buildTaskData(self, pkgs_to_build, task, halt, allowincomplete=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500658 """
659 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
660 """
661 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
662
663 # A task of None means use the default task
664 if task is None:
665 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500666 if not task.startswith("do_"):
667 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500668
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500669 targetlist = self.checkPackages(pkgs_to_build, task)
670 fulltargetlist = []
671 defaulttask_implicit = ''
672 defaulttask_explicit = False
673 wildcard = False
674
675 # Wild card expansion:
Brad Bishop15ae2502019-06-18 21:44:24 -0400676 # Replace string such as "mc:*:bash"
677 # into "mc:A:bash mc:B:bash bash"
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500678 for k in targetlist:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600679 if k.startswith("mc:") and k.count(':') >= 2:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500680 if wildcard:
681 bb.fatal('multiconfig conflict')
682 if k.split(":")[1] == "*":
683 wildcard = True
684 for mc in self.multiconfigs:
685 if mc:
686 fulltargetlist.append(k.replace('*', mc))
687 # implicit default task
688 else:
689 defaulttask_implicit = k.split(":")[2]
690 else:
691 fulltargetlist.append(k)
692 else:
693 defaulttask_explicit = True
694 fulltargetlist.append(k)
695
696 if not defaulttask_explicit and defaulttask_implicit != '':
697 fulltargetlist.append(defaulttask_implicit)
698
699 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600700 taskdata = {}
701 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500702
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600703 for mc in self.multiconfigs:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000704 taskdata[mc] = bb.taskdata.TaskData(halt, skiplist=self.skiplist, allowincomplete=allowincomplete)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600705 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600706 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707
708 current = 0
709 runlist = []
710 for k in fulltargetlist:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500711 origk = k
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600712 mc = ""
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600713 if k.startswith("mc:") and k.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600714 mc = k.split(":")[1]
715 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500716 ktask = task
717 if ":do_" in k:
718 k2 = k.split(":do_")
719 k = k2[0]
720 ktask = k2[1]
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500721
722 if mc not in self.multiconfigs:
723 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named %s" % (origk, mc))
724
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600725 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500726 current += 1
727 if not ktask.startswith("do_"):
728 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600729 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
730 # e.g. in ASSUME_PROVIDED
731 continue
732 fn = taskdata[mc].build_targets[k][0]
733 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500734 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600735
Brad Bishop15ae2502019-06-18 21:44:24 -0400736 havemc = False
737 for mc in self.multiconfigs:
738 if taskdata[mc].get_mcdepends():
739 havemc = True
Brad Bishopf058f492019-01-28 23:50:33 -0500740
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800741 # No need to do check providers if there are no mcdeps or not an mc build
Brad Bishop15ae2502019-06-18 21:44:24 -0400742 if havemc or len(self.multiconfigs) > 1:
Andrew Geissler99467da2019-02-25 18:54:23 -0600743 seen = set()
744 new = True
745 # Make sure we can provide the multiconfig dependency
746 while new:
747 mcdeps = set()
748 # Add unresolved first, so we can get multiconfig indirect dependencies on time
749 for mc in self.multiconfigs:
750 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
751 mcdeps |= set(taskdata[mc].get_mcdepends())
752 new = False
Patrick Williams03907ee2022-05-01 06:28:52 -0500753 for k in mcdeps:
754 if k in seen:
755 continue
756 l = k.split(':')
757 depmc = l[2]
758 if depmc not in self.multiconfigs:
759 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named configuration %s" % (k,depmc))
760 else:
761 logger.debug("Adding providers for multiconfig dependency %s" % l[3])
762 taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
763 seen.add(k)
764 new = True
Brad Bishopf058f492019-01-28 23:50:33 -0500765
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600766 for mc in self.multiconfigs:
767 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
768
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500769 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600770 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771
772 def prepareTreeData(self, pkgs_to_build, task):
773 """
774 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
775 """
776
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000777 # We set halt to False here to prevent unbuildable targets raising
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500778 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600779 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500780
781 return runlist, taskdata
782
783 ######## WARNING : this function requires cache_extra to be enabled ########
784
785 def generateTaskDepTreeData(self, pkgs_to_build, task):
786 """
787 Create a dependency graph of pkgs_to_build including reverse dependency
788 information.
789 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500790 if not task.startswith("do_"):
791 task = "do_%s" % task
792
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500793 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600794 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500795 rq.rqdata.prepare()
796 return self.buildDependTree(rq, taskdata)
797
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 @staticmethod
799 def add_mc_prefix(mc, pn):
800 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -0400801 return "mc:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600802 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500803
804 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600805 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500806 depend_tree = {}
807 depend_tree["depends"] = {}
808 depend_tree["tdepends"] = {}
809 depend_tree["pn"] = {}
810 depend_tree["rdepends-pn"] = {}
811 depend_tree["packages"] = {}
812 depend_tree["rdepends-pkg"] = {}
813 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500814 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600815 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500816
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600817 for mc in taskdata:
818 for name, fn in list(taskdata[mc].get_providermap().items()):
819 pn = self.recipecaches[mc].pkg_fn[fn]
820 pn = self.add_mc_prefix(mc, pn)
821 if name != pn:
822 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
823 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500824
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600825 for tid in rq.rqdata.runtaskentries:
826 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
827 pn = self.recipecaches[mc].pkg_fn[taskfn]
828 pn = self.add_mc_prefix(mc, pn)
829 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500830 if pn not in depend_tree["pn"]:
831 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600832 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600834 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500835
836 # if we have extra caches, list all attributes they bring in
837 extra_info = []
838 for cache_class in self.caches_array:
839 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
840 cachefields = getattr(cache_class, 'cachefields', [])
841 extra_info = extra_info + cachefields
842
843 # for all attributes stored, add them to the dependency tree
844 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600845 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500846
847
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500848 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
849 if not dotname in depend_tree["tdepends"]:
850 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600851 for dep in rq.rqdata.runtaskentries[tid].depends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800852 (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
853 deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
Andrew Geissler595f6302022-01-24 19:11:47 +0000854 if depmc:
855 depmc = "mc:" + depmc + ":"
856 depend_tree["tdepends"][dotname].append("%s%s.%s" % (depmc, deppn, bb.runqueue.taskname_from_tid(dep)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600857 if taskfn not in seen_fns:
858 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500859 packages = []
860
861 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600862 for dep in taskdata[mc].depids[taskfn]:
863 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500864
865 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600866 for rdep in taskdata[mc].rdepids[taskfn]:
867 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500868
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600869 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500870 for package in rdepends:
871 depend_tree["rdepends-pkg"][package] = []
872 for rdepend in rdepends[package]:
873 depend_tree["rdepends-pkg"][package].append(rdepend)
874 packages.append(package)
875
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600876 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500877 for package in rrecs:
878 depend_tree["rrecs-pkg"][package] = []
879 for rdepend in rrecs[package]:
880 depend_tree["rrecs-pkg"][package].append(rdepend)
881 if not package in packages:
882 packages.append(package)
883
884 for package in packages:
885 if package not in depend_tree["packages"]:
886 depend_tree["packages"][package] = {}
887 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600888 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500889 depend_tree["packages"][package]["version"] = version
890
891 return depend_tree
892
893 ######## WARNING : this function requires cache_extra to be enabled ########
894 def generatePkgDepTreeData(self, pkgs_to_build, task):
895 """
896 Create a dependency tree of pkgs_to_build, returning the data.
897 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500898 if not task.startswith("do_"):
899 task = "do_%s" % task
900
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500901 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500902
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600903 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500904 depend_tree = {}
905 depend_tree["depends"] = {}
906 depend_tree["pn"] = {}
907 depend_tree["rdepends-pn"] = {}
908 depend_tree["rdepends-pkg"] = {}
909 depend_tree["rrecs-pkg"] = {}
910
911 # if we have extra caches, list all attributes they bring in
912 extra_info = []
913 for cache_class in self.caches_array:
914 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
915 cachefields = getattr(cache_class, 'cachefields', [])
916 extra_info = extra_info + cachefields
917
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600918 tids = []
919 for mc in taskdata:
920 for tid in taskdata[mc].taskentries:
921 tids.append(tid)
922
923 for tid in tids:
924 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
925
926 pn = self.recipecaches[mc].pkg_fn[taskfn]
927 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500928
929 if pn not in depend_tree["pn"]:
930 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600931 depend_tree["pn"][pn]["filename"] = taskfn
932 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500933 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600934 rdepends = self.recipecaches[mc].rundeps[taskfn]
935 rrecs = self.recipecaches[mc].runrecs[taskfn]
936 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500937
938 # for all extra attributes stored, add them to the dependency tree
939 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600940 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500941
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600942 if taskfn not in seen_fns:
943 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500944
945 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500946 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500947 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600948 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
949 fn_provider = taskdata[mc].build_targets[dep][0]
950 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500951 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500952 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600953 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500954 depend_tree["depends"][pn].append(pn_provider)
955
956 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600957 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500958 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600959 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
960 fn_rprovider = taskdata[mc].run_targets[rdep][0]
961 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500962 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600963 pn_rprovider = rdep
964 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500965 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
966
967 depend_tree["rdepends-pkg"].update(rdepends)
968 depend_tree["rrecs-pkg"].update(rrecs)
969
970 return depend_tree
971
972 def generateDepTreeEvent(self, pkgs_to_build, task):
973 """
974 Create a task dependency graph of pkgs_to_build.
975 Generate an event with the result
976 """
977 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
978 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
979
980 def generateDotGraphFiles(self, pkgs_to_build, task):
981 """
982 Create a task dependency graph of pkgs_to_build.
983 Save the result to a set of .dot files.
984 """
985
986 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
987
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500988 with open('pn-buildlist', 'w') as f:
989 for pn in depgraph["pn"]:
990 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500991 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500992
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500993 # Remove old format output files to ensure no confusion with stale data
994 try:
995 os.unlink('pn-depends.dot')
996 except FileNotFoundError:
997 pass
998 try:
999 os.unlink('package-depends.dot')
1000 except FileNotFoundError:
1001 pass
Brad Bishop79641f22019-09-10 07:20:22 -04001002 try:
1003 os.unlink('recipe-depends.dot')
1004 except FileNotFoundError:
1005 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001006
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001007 with open('task-depends.dot', 'w') as f:
1008 f.write("digraph depends {\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -04001009 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001010 (pn, taskname) = task.rsplit(".", 1)
1011 fn = depgraph["pn"][pn]["filename"]
1012 version = depgraph["pn"][pn]["version"]
1013 f.write('"%s.%s" [label="%s %s\\n%s\\n%s"]\n' % (pn, taskname, pn, taskname, version, fn))
Brad Bishop316dfdd2018-06-25 12:45:53 -04001014 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001015 f.write('"%s" -> "%s"\n' % (task, dep))
1016 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001017 logger.info("Task dependencies saved to 'task-depends.dot'")
1018
1019 def show_appends_with_no_recipes(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001020 appends_without_recipes = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001021 # Determine which bbappends haven't been applied
Andrew Geissler5a43b432020-06-13 10:46:56 -05001022 for mc in self.multiconfigs:
1023 # First get list of recipes, including skipped
1024 recipefns = list(self.recipecaches[mc].pkg_fn.keys())
1025 recipefns.extend(self.skiplist.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001026
Andrew Geissler5a43b432020-06-13 10:46:56 -05001027 # Work out list of bbappends that have been applied
1028 applied_appends = []
1029 for fn in recipefns:
1030 applied_appends.extend(self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001031
Andrew Geissler5a43b432020-06-13 10:46:56 -05001032 appends_without_recipes[mc] = []
1033 for _, appendfn in self.collections[mc].bbappends:
1034 if not appendfn in applied_appends:
1035 appends_without_recipes[mc].append(appendfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001036
Andrew Geissler5a43b432020-06-13 10:46:56 -05001037 msgs = []
1038 for mc in sorted(appends_without_recipes.keys()):
1039 if appends_without_recipes[mc]:
1040 msgs.append('No recipes in %s available for:\n %s' % (mc if mc else 'default',
1041 '\n '.join(appends_without_recipes[mc])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001042
Andrew Geissler5a43b432020-06-13 10:46:56 -05001043 if msgs:
1044 msg = "\n".join(msgs)
1045 warn_only = self.databuilder.mcdata[mc].getVar("BB_DANGLINGAPPENDS_WARNONLY", \
1046 False) or "no"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001047 if warn_only.lower() in ("1", "yes", "true"):
1048 bb.warn(msg)
1049 else:
1050 bb.fatal(msg)
1051
1052 def handlePrefProviders(self):
1053
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001054 for mc in self.multiconfigs:
1055 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001056 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001057
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001058 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001059 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001060 try:
1061 (providee, provider) = p.split(':')
1062 except:
1063 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
1064 continue
1065 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
1066 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
1067 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001068
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001069 def findConfigFilePath(self, configfile):
1070 """
1071 Find the location on disk of configfile and if it exists and was parsed by BitBake
1072 emit the ConfigFilePathFound event with the path to the file.
1073 """
1074 path = bb.cookerdata.findConfigFile(configfile, self.data)
1075 if not path:
1076 return
1077
1078 # Generate a list of parsed configuration files by searching the files
1079 # listed in the __depends and __base_depends variables with a .conf suffix.
1080 conffiles = []
1081 dep_files = self.data.getVar('__base_depends', False) or []
1082 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
1083
1084 for f in dep_files:
1085 if f[0].endswith(".conf"):
1086 conffiles.append(f[0])
1087
1088 _, conf, conffile = path.rpartition("conf/")
1089 match = os.path.join(conf, conffile)
1090 # Try and find matches for conf/conffilename.conf as we don't always
1091 # have the full path to the file.
1092 for cfg in conffiles:
1093 if cfg.endswith(match):
1094 bb.event.fire(bb.event.ConfigFilePathFound(path),
1095 self.data)
1096 break
1097
1098 def findFilesMatchingInDir(self, filepattern, directory):
1099 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001100 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001101 'directory' in each BBPATH. i.e. to find all rootfs package classes available
1102 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
1103 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001104 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001105 """
1106
1107 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001108 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001109 for path in bbpaths:
1110 dirpath = os.path.join(path, directory)
1111 if os.path.exists(dirpath):
1112 for root, dirs, files in os.walk(dirpath):
1113 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001114 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001115 matches.append(f)
1116
1117 if matches:
1118 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1119
Patrick Williams93c203f2021-10-06 16:15:23 -05001120 def testCookerCommandEvent(self, filepattern):
1121 # Dummy command used by OEQA selftest to test tinfoil without IO
1122 matches = ["A", "B"]
1123 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1124
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001125 def findProviders(self, mc=''):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001126 return bb.providers.findProviders(self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001127
1128 def findBestProvider(self, pn, mc=''):
1129 if pn in self.recipecaches[mc].providers:
1130 filenames = self.recipecaches[mc].providers[pn]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001131 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.databuilder.mcdata[mc], self.recipecaches[mc])
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001132 if eligible is not None:
1133 filename = eligible[0]
1134 else:
1135 filename = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001136 return None, None, None, filename
1137 elif pn in self.recipecaches[mc].pkg_pn:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001138 (latest, latest_f, preferred_ver, preferred_file, required) = bb.providers.findBestProvider(pn, self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
1139 if required and preferred_file is None:
1140 return None, None, None, None
1141 return (latest, latest_f, preferred_ver, preferred_file)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001142 else:
1143 return None, None, None, None
1144
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001145 def findConfigFiles(self, varname):
1146 """
1147 Find config files which are appropriate values for varname.
1148 i.e. MACHINE, DISTRO
1149 """
1150 possible = []
1151 var = varname.lower()
1152
1153 data = self.data
1154 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001155 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001156 for path in bbpaths:
1157 confpath = os.path.join(path, "conf", var)
1158 if os.path.exists(confpath):
1159 for root, dirs, files in os.walk(confpath):
1160 # get all child files, these are appropriate values
1161 for f in files:
1162 val, sep, end = f.rpartition('.')
1163 if end == 'conf':
1164 possible.append(val)
1165
1166 if possible:
1167 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1168
1169 def findInheritsClass(self, klass):
1170 """
1171 Find all recipes which inherit the specified class
1172 """
1173 pkg_list = []
1174
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001175 for pfn in self.recipecaches[''].pkg_fn:
1176 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001177 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001178 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001179
1180 return pkg_list
1181
1182 def generateTargetsTree(self, klass=None, pkgs=None):
1183 """
1184 Generate a dependency tree of buildable targets
1185 Generate an event with the result
1186 """
1187 # if the caller hasn't specified a pkgs list default to universe
1188 if not pkgs:
1189 pkgs = ['universe']
1190 # if inherited_class passed ensure all recipes which inherit the
1191 # specified class are included in pkgs
1192 if klass:
1193 extra_pkgs = self.findInheritsClass(klass)
1194 pkgs = pkgs + extra_pkgs
1195
1196 # generate a dependency tree for all our packages
1197 tree = self.generatePkgDepTreeData(pkgs, 'build')
1198 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1199
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001200 def interactiveMode( self ):
1201 """Drop off into a shell"""
1202 try:
1203 from bb import shell
1204 except ImportError:
1205 parselog.exception("Interactive mode not available")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001206 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001207 else:
1208 shell.start( self )
1209
1210
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001211 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001212 """Handle collections"""
1213 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001214 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001215 if collections:
1216 collection_priorities = {}
1217 collection_depends = {}
1218 collection_list = collections.split()
1219 min_prio = 0
1220 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001221 bb.debug(1,'Processing %s in collection list' % (c))
1222
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001223 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001224 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001225 if priority:
1226 try:
1227 prio = int(priority)
1228 except ValueError:
1229 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1230 errors = True
1231 if min_prio == 0 or prio < min_prio:
1232 min_prio = prio
1233 collection_priorities[c] = prio
1234 else:
1235 collection_priorities[c] = None
1236
1237 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001238 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001239 if deps:
1240 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001241 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001242 except bb.utils.VersionStringException as vse:
1243 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001244 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001245 if dep in collection_list:
1246 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001247 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001248 (op, depver) = opstr.split()
1249 if layerver:
1250 try:
1251 res = bb.utils.vercmp_string_op(layerver, depver, op)
1252 except bb.utils.VersionStringException as vse:
1253 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1254 if not res:
1255 parselog.error("Layer '%s' depends on version %s of layer '%s', but version %s is currently enabled in your configuration. Check that you are using the correct matching versions/branches of these two layers.", c, opstr, dep, layerver)
1256 errors = True
1257 else:
1258 parselog.error("Layer '%s' depends on version %s of layer '%s', which exists in your configuration but does not specify a version. Check that you are using the correct matching versions/branches of these two layers.", c, opstr, dep)
1259 errors = True
1260 else:
1261 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1262 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001263 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001264 else:
1265 collection_depends[c] = []
1266
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001267 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001268 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001269 if recs:
1270 try:
1271 recDict = bb.utils.explode_dep_versions2(recs)
1272 except bb.utils.VersionStringException as vse:
1273 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1274 for rec, oplist in list(recDict.items()):
1275 if rec in collection_list:
1276 if oplist:
1277 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001278 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001279 if layerver:
1280 (op, recver) = opstr.split()
1281 try:
1282 res = bb.utils.vercmp_string_op(layerver, recver, op)
1283 except bb.utils.VersionStringException as vse:
1284 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1285 if not res:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001286 parselog.debug3("Layer '%s' recommends version %s of layer '%s', but version %s is currently enabled in your configuration. Check that you are using the correct matching versions/branches of these two layers.", c, opstr, rec, layerver)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001287 continue
1288 else:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001289 parselog.debug3("Layer '%s' recommends version %s of layer '%s', which exists in your configuration but does not specify a version. Check that you are using the correct matching versions/branches of these two layers.", c, opstr, rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001290 continue
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001291 parselog.debug3("Layer '%s' recommends layer '%s', so we are adding it", c, rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001292 collection_depends[c].append(rec)
1293 else:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001294 parselog.debug3("Layer '%s' recommends layer '%s', but this layer is not enabled in your configuration", c, rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001295
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001296 # Recursively work out collection priorities based on dependencies
1297 def calc_layer_priority(collection):
1298 if not collection_priorities[collection]:
1299 max_depprio = min_prio
1300 for dep in collection_depends[collection]:
1301 calc_layer_priority(dep)
1302 depprio = collection_priorities[dep]
1303 if depprio > max_depprio:
1304 max_depprio = depprio
1305 max_depprio += 1
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001306 parselog.debug("Calculated priority of layer %s as %d", collection, max_depprio)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001307 collection_priorities[collection] = max_depprio
1308
1309 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1310 for c in collection_list:
1311 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001312 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001313 if regex is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001314 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1315 errors = True
1316 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001317 elif regex == "":
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001318 parselog.debug("BBFILE_PATTERN_%s is empty" % c)
Brad Bishop19323692019-04-05 15:28:33 -04001319 cre = re.compile('^NULL$')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001320 errors = False
1321 else:
1322 try:
1323 cre = re.compile(regex)
1324 except re.error:
1325 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1326 errors = True
1327 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001328 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001329 if errors:
1330 # We've already printed the actual error(s)
1331 raise CollectionError("Errors during parsing layer configuration")
1332
1333 def buildSetVars(self):
1334 """
1335 Setup any variables needed before starting a build
1336 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001337 t = time.gmtime()
1338 for mc in self.databuilder.mcdata:
1339 ds = self.databuilder.mcdata[mc]
1340 if not ds.getVar("BUILDNAME", False):
1341 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1342 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1343 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1344 ds.setVar("TIME", time.strftime('%H%M%S', t))
1345
1346 def reset_mtime_caches(self):
1347 """
1348 Reset mtime caches - this is particularly important when memory resident as something
1349 which is cached is not unlikely to have changed since the last invocation (e.g. a
1350 file associated with a recipe might have been modified by the user).
1351 """
1352 build.reset_cache()
1353 bb.fetch._checksum_cache.mtime_cache.clear()
1354 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1355 if siggen_cache:
1356 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001357
Andrew Geissler5a43b432020-06-13 10:46:56 -05001358 def matchFiles(self, bf, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001359 """
1360 Find the .bb files which match the expression in 'buildfile'.
1361 """
1362 if bf.startswith("/") or bf.startswith("../"):
1363 bf = os.path.abspath(bf)
1364
Andrew Geissler5a43b432020-06-13 10:46:56 -05001365 self.collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)}
1366 filelist, masked, searchdirs = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001367 try:
1368 os.stat(bf)
1369 bf = os.path.abspath(bf)
1370 return [bf]
1371 except OSError:
1372 regexp = re.compile(bf)
1373 matches = []
1374 for f in filelist:
1375 if regexp.search(f) and os.path.isfile(f):
1376 matches.append(f)
1377 return matches
1378
Andrew Geissler5a43b432020-06-13 10:46:56 -05001379 def matchFile(self, buildfile, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001380 """
1381 Find the .bb file which matches the expression in 'buildfile'.
1382 Raise an error if multiple files
1383 """
Andrew Geissler5a43b432020-06-13 10:46:56 -05001384 matches = self.matchFiles(buildfile, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001385 if len(matches) != 1:
1386 if matches:
1387 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1388 if matches:
1389 for f in matches:
1390 msg += "\n %s" % f
1391 parselog.error(msg)
1392 else:
1393 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1394 raise NoSpecificMatch
1395 return matches[0]
1396
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001397 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001398 """
1399 Build the file matching regexp buildfile
1400 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001401 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001402
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001403 # Too many people use -b because they think it's how you normally
1404 # specify a target to be built, so show a warning
1405 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1406
1407 self.buildFileInternal(buildfile, task)
1408
1409 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1410 """
1411 Build the file matching regexp buildfile
1412 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001413
1414 # Parse the configuration here. We need to do it explicitly here since
1415 # buildFile() doesn't use the cache
1416 self.parseConfiguration()
1417
1418 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001419 if task is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001420 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001421 if not task.startswith("do_"):
1422 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001423
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001424 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001425 fn = self.matchFile(fn, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001426
1427 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001428 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001429
Andrew Geissler5a43b432020-06-13 10:46:56 -05001430 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001431
Andrew Geissler5a43b432020-06-13 10:46:56 -05001432 infos = bb_caches[mc].parse(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001433 infos = dict(infos)
1434
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001435 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001436 try:
1437 info_array = infos[fn]
1438 except KeyError:
1439 bb.fatal("%s does not exist" % fn)
1440
1441 if info_array[0].skipped:
1442 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1443
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001444 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001445
1446 # Tweak some variables
1447 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001448 self.recipecaches[mc].ignored_dependencies = set()
1449 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001450 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001451
1452 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001453 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1454 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001455 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1456 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001457
Andrew Geissler517393d2023-01-13 08:55:19 -06001458 bb.parse.siggen.setup_datacache(self.recipecaches)
1459
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001460 # Invalidate task for target if force mode active
1461 if self.configuration.force:
1462 logger.verbose("Invalidate task %s, %s", task, fn)
Andrew Geissler517393d2023-01-13 08:55:19 -06001463 bb.parse.siggen.invalidate_task(task, fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001464
1465 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001466 taskdata = {}
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001467 taskdata[mc] = bb.taskdata.TaskData(self.configuration.halt)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001468 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001469
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001470 if quietlog:
1471 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1472 bb.runqueue.logger.setLevel(logging.WARNING)
1473
1474 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1475 if fireevents:
1476 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Andrew Geissler517393d2023-01-13 08:55:19 -06001477 bb.event.enable_heartbeat()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001478
1479 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001480 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001481
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001482 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001483
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001484 def buildFileIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001485
1486 msg = None
1487 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001488 if halt or self.state == state.forceshutdown:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001489 rq.finish_runqueue(True)
1490 msg = "Forced shutdown"
1491 interrupted = 2
1492 elif self.state == state.shutdown:
1493 rq.finish_runqueue(False)
1494 msg = "Stopped build"
1495 interrupted = 1
1496 failures = 0
1497 try:
1498 retval = rq.execute_runqueue()
1499 except runqueue.TaskFailure as exc:
1500 failures += len(exc.args)
1501 retval = False
1502 except SystemExit as exc:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001503 if quietlog:
1504 bb.runqueue.logger.setLevel(rqloglevel)
Andrew Geissler517393d2023-01-13 08:55:19 -06001505 return bb.server.process.idleFinish(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001506
1507 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001508 if fireevents:
1509 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, item, failures, interrupted), self.databuilder.mcdata[mc])
Andrew Geissler517393d2023-01-13 08:55:19 -06001510 bb.event.disable_heartbeat()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001511 # We trashed self.recipecaches above
1512 self.parsecache_valid = False
1513 self.configuration.limited_deps = False
1514 bb.parse.siggen.reset(self.data)
1515 if quietlog:
1516 bb.runqueue.logger.setLevel(rqloglevel)
Andrew Geissler517393d2023-01-13 08:55:19 -06001517 return bb.server.process.idleFinish(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001518 if retval is True:
1519 return True
1520 return retval
1521
Andrew Geissler635e0e42020-08-21 15:58:33 -05001522 self.idleCallBackRegister(buildFileIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001523
1524 def buildTargets(self, targets, task):
1525 """
1526 Attempt to build the targets specified
1527 """
1528
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001529 def buildTargetsIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001530 msg = None
1531 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001532 if halt or self.state == state.forceshutdown:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001533 bb.event._should_exit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001534 rq.finish_runqueue(True)
1535 msg = "Forced shutdown"
1536 interrupted = 2
1537 elif self.state == state.shutdown:
1538 rq.finish_runqueue(False)
1539 msg = "Stopped build"
1540 interrupted = 1
1541 failures = 0
1542 try:
1543 retval = rq.execute_runqueue()
1544 except runqueue.TaskFailure as exc:
1545 failures += len(exc.args)
1546 retval = False
1547 except SystemExit as exc:
Andrew Geissler517393d2023-01-13 08:55:19 -06001548 return bb.server.process.idleFinish(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001549
1550 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001551 try:
1552 for mc in self.multiconfigs:
1553 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1554 finally:
Andrew Geissler517393d2023-01-13 08:55:19 -06001555 bb.event.disable_heartbeat()
1556 return bb.server.process.idleFinish(msg)
1557
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001558 if retval is True:
1559 return True
1560 return retval
1561
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001562 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001563 self.buildSetVars()
1564
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001565 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001566 if task is None:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001567 task = self.configuration.cmd
1568
1569 if not task.startswith("do_"):
1570 task = "do_%s" % task
1571
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001572 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1573
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001574 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001575
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001576 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.halt)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001577
1578 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001579
1580 # make targets to always look as <target>:do_<task>
1581 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001582 for target in runlist:
1583 if target[0]:
Brad Bishop15ae2502019-06-18 21:44:24 -04001584 ntargets.append("mc:%s:%s:%s" % (target[0], target[1], target[2]))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001585 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001586
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001587 for mc in self.multiconfigs:
1588 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Andrew Geissler517393d2023-01-13 08:55:19 -06001589 bb.event.enable_heartbeat()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001590
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001591 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001592 if 'universe' in targets:
1593 rq.rqdata.warn_multi_bb = True
1594
Andrew Geissler635e0e42020-08-21 15:58:33 -05001595 self.idleCallBackRegister(buildTargetsIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001596
1597
1598 def getAllKeysWithFlags(self, flaglist):
1599 dump = {}
1600 for k in self.data.keys():
1601 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001602 expand = True
1603 flags = self.data.getVarFlags(k)
1604 if flags and "func" in flags and "python" in flags:
1605 expand = False
1606 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001607 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1608 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001609 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001610 'history' : self.data.varhistory.variable(k),
1611 }
1612 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001613 if flags and d in flags:
1614 dump[k][d] = flags[d]
1615 else:
1616 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001617 except Exception as e:
1618 print(e)
1619 return dump
1620
1621
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001622 def updateCacheSync(self):
1623 if self.state == state.running:
1624 return
1625
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001626 self.handle_inotify_updates()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001627
1628 if not self.baseconfig_valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001629 logger.debug("Reloading base configuration data")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001630 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001631 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001632
1633 # This is called for all async commands when self.state != running
1634 def updateCache(self):
1635 if self.state == state.running:
1636 return
1637
1638 if self.state in (state.shutdown, state.forceshutdown, state.error):
1639 if hasattr(self.parser, 'shutdown'):
Andrew Geissler9aee5002022-03-30 16:27:02 +00001640 self.parser.shutdown(clean=False)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001641 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001642 raise bb.BBHandledException()
1643
1644 if self.state != state.parsing:
1645 self.updateCacheSync()
1646
1647 if self.state != state.parsing and not self.parsecache_valid:
Patrick Williamsde0582f2022-04-08 10:23:27 -05001648 self.setupParserWatcher()
1649
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001650 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001651 self.parseConfiguration ()
1652 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001653 for mc in self.multiconfigs:
1654 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001655
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001656 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001657 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001658 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001659
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001660 for dep in self.configuration.extra_assume_provided:
1661 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001662
Andrew Geissler5a43b432020-06-13 10:46:56 -05001663 self.collections = {}
1664
1665 mcfilelist = {}
1666 total_masked = 0
1667 searchdirs = set()
1668 for mc in self.multiconfigs:
1669 self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc)
1670 (filelist, masked, search) = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
1671
1672 mcfilelist[mc] = filelist
1673 total_masked += masked
1674 searchdirs |= set(search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001675
1676 # Add inotify watches for directories searched for bb/bbappend files
1677 for dirent in searchdirs:
1678 self.add_filewatch([[dirent]], dirs=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001679
Andrew Geissler5a43b432020-06-13 10:46:56 -05001680 self.parser = CookerParser(self, mcfilelist, total_masked)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001681 self.parsecache_valid = True
1682
1683 self.state = state.parsing
1684
1685 if not self.parser.parse_next():
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001686 collectlog.debug("parsing complete")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001687 if self.parser.error:
1688 raise bb.BBHandledException()
1689 self.show_appends_with_no_recipes()
1690 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001691 for mc in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001692 self.recipecaches[mc].bbfile_priority = self.collections[mc].collection_priorities(self.recipecaches[mc].pkg_fn, self.parser.mcfilelist[mc], self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001693 self.state = state.running
1694
1695 # Send an event listing all stamps reachable after parsing
1696 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001697 for mc in self.multiconfigs:
1698 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1699 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001700 return None
1701
1702 return True
1703
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001704 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001705
1706 # Return a copy, don't modify the original
1707 pkgs_to_build = pkgs_to_build[:]
1708
Andrew Geissler595f6302022-01-24 19:11:47 +00001709 if not pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001710 raise NothingToBuild
1711
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001712 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001713 for pkg in pkgs_to_build.copy():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001714 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001715 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Brad Bishop15ae2502019-06-18 21:44:24 -04001716 if pkg.startswith("multiconfig:"):
1717 pkgs_to_build.remove(pkg)
1718 pkgs_to_build.append(pkg.replace("multiconfig:", "mc:"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001719
1720 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001721 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001722 for mc in self.multiconfigs:
1723 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1724 for t in self.recipecaches[mc].world_target:
1725 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001726 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001727 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001728
1729 if 'universe' in pkgs_to_build:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001730 parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001731 parselog.debug("collating packages for \"universe\"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001732 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001733 for mc in self.multiconfigs:
1734 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001735 if task:
1736 foundtask = False
1737 for provider_fn in self.recipecaches[mc].providers[t]:
1738 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1739 foundtask = True
1740 break
1741 if not foundtask:
1742 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1743 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001744 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001745 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001746 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001747
1748 return pkgs_to_build
1749
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001750 def pre_serve(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001751 return
1752
1753 def post_serve(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001754 self.shutdown(force=True)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001755 prserv.serv.auto_shutdown()
Patrick Williams45852732022-04-02 08:58:32 -05001756 if hasattr(bb.parse, "siggen"):
1757 bb.parse.siggen.exit()
Brad Bishop08902b02019-08-20 09:16:51 -04001758 if self.hashserv:
1759 self.hashserv.process.terminate()
1760 self.hashserv.process.join()
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001761 if hasattr(self, "data"):
1762 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001763
Andrew Geissler517393d2023-01-13 08:55:19 -06001764 def shutdown(self, force=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001765 if force:
1766 self.state = state.forceshutdown
1767 else:
1768 self.state = state.shutdown
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001769 bb.event._should_exit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001770
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001771 if self.parser:
Andrew Geissler517393d2023-01-13 08:55:19 -06001772 self.parser.shutdown(clean=False)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001773 self.parser.final_cleanup()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001774
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001775 def finishcommand(self):
Andrew Geissler517393d2023-01-13 08:55:19 -06001776 if hasattr(self.parser, 'shutdown'):
1777 self.parser.shutdown(clean=False)
1778 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001779 self.state = state.initial
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001780 bb.event._should_exit.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001781
1782 def reset(self):
Patrick Williams45852732022-04-02 08:58:32 -05001783 if hasattr(bb.parse, "siggen"):
1784 bb.parse.siggen.exit()
Andrew Geissler517393d2023-01-13 08:55:19 -06001785 self.finishcommand()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001786 self.initConfigurationData()
Brad Bishop08902b02019-08-20 09:16:51 -04001787 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001788
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001789 def clientComplete(self):
1790 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001791 self.finishcommand()
1792 self.extraconfigdata = {}
1793 self.command.reset()
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001794 if hasattr(self, "data"):
1795 self.databuilder.reset()
1796 self.data = self.databuilder.data
Andrew Geissler517393d2023-01-13 08:55:19 -06001797 # In theory tinfoil could have modified the base data before parsing,
1798 # ideally need to track if anything did modify the datastore
Andrew Geissler82c905d2020-04-13 13:39:40 -05001799 self.parsecache_valid = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001800
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001801
1802class CookerExit(bb.event.Event):
1803 """
1804 Notify clients of the Cooker shutdown
1805 """
1806
1807 def __init__(self):
1808 bb.event.Event.__init__(self)
1809
1810
1811class CookerCollectFiles(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001812 def __init__(self, priorities, mc=''):
1813 self.mc = mc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001814 self.bbappends = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001815 # Priorities is a list of tuples, with the second element as the pattern.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001816 # We need to sort the list with the longest pattern first, and so on to
1817 # the shortest. This allows nested layers to be properly evaluated.
1818 self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001819
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001820 def calc_bbfile_priority(self, filename):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001821 for _, _, regex, pri in self.bbfile_config_priorities:
1822 if regex.match(filename):
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001823 return pri, regex
1824 return 0, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001825
1826 def get_bbfiles(self):
1827 """Get list of default .bb files by reading out the current directory"""
1828 path = os.getcwd()
1829 contents = os.listdir(path)
1830 bbfiles = []
1831 for f in contents:
1832 if f.endswith(".bb"):
1833 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1834 return bbfiles
1835
1836 def find_bbfiles(self, path):
1837 """Find all the .bb and .bbappend files in a directory"""
1838 found = []
1839 for dir, dirs, files in os.walk(path):
1840 for ignored in ('SCCS', 'CVS', '.svn'):
1841 if ignored in dirs:
1842 dirs.remove(ignored)
1843 found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
1844
1845 return found
1846
1847 def collect_bbfiles(self, config, eventdata):
1848 """Collect all available .bb build files"""
1849 masked = 0
1850
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001851 collectlog.debug("collecting .bb files")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001852
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001853 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001854
1855 # Sort files by priority
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001856 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem)[0] )
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001857 config.setVar("BBFILES_PRIORITIZED", " ".join(files))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001858
Andrew Geissler595f6302022-01-24 19:11:47 +00001859 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001860 files = self.get_bbfiles()
1861
Andrew Geissler595f6302022-01-24 19:11:47 +00001862 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001863 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1864 bb.event.fire(CookerExit(), eventdata)
1865
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001866 # We need to track where we look so that we can add inotify watches. There
1867 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001868 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001869 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001870 if hasattr(os, 'scandir'):
1871 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001872 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001873
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001874 def ourlistdir(d):
1875 searchdirs.append(d)
1876 return origlistdir(d)
1877
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001878 def ourscandir(d):
1879 searchdirs.append(d)
1880 return origscandir(d)
1881
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001882 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001883 if hasattr(os, 'scandir'):
1884 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001885 try:
1886 # Can't use set here as order is important
1887 newfiles = []
1888 for f in files:
1889 if os.path.isdir(f):
1890 dirfiles = self.find_bbfiles(f)
1891 for g in dirfiles:
1892 if g not in newfiles:
1893 newfiles.append(g)
1894 else:
1895 globbed = glob.glob(f)
1896 if not globbed and os.path.exists(f):
1897 globbed = [f]
1898 # glob gives files in order on disk. Sort to be deterministic.
1899 for g in sorted(globbed):
1900 if g not in newfiles:
1901 newfiles.append(g)
1902 finally:
1903 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001904 if hasattr(os, 'scandir'):
1905 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001906
1907 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001908
1909 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001910 # First validate the individual regular expressions and ignore any
1911 # that do not compile
1912 bbmasks = []
1913 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001914 # When constructing an older style single regex, it's possible for BBMASK
1915 # to end up beginning with '|', which matches and masks _everything_.
1916 if mask.startswith("|"):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001917 collectlog.warning("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001918 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001919 try:
1920 re.compile(mask)
1921 bbmasks.append(mask)
Andrew Geissler78b72792022-06-14 06:47:25 -05001922 except re.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001923 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1924
1925 # Then validate the combined regular expressions. This should never
1926 # fail, but better safe than sorry...
1927 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001928 try:
1929 bbmask_compiled = re.compile(bbmask)
Andrew Geissler78b72792022-06-14 06:47:25 -05001930 except re.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001931 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1932 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001933
1934 bbfiles = []
1935 bbappend = []
1936 for f in newfiles:
1937 if bbmask and bbmask_compiled.search(f):
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001938 collectlog.debug("skipping masked file %s", f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001939 masked += 1
1940 continue
1941 if f.endswith('.bb'):
1942 bbfiles.append(f)
1943 elif f.endswith('.bbappend'):
1944 bbappend.append(f)
1945 else:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001946 collectlog.debug("skipping %s: unknown file extension", f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001947
1948 # Build a list of .bbappend files for each .bb file
1949 for f in bbappend:
1950 base = os.path.basename(f).replace('.bbappend', '.bb')
1951 self.bbappends.append((base, f))
1952
1953 # Find overlayed recipes
1954 # bbfiles will be in priority order which makes this easy
1955 bbfile_seen = dict()
1956 self.overlayed = defaultdict(list)
1957 for f in reversed(bbfiles):
1958 base = os.path.basename(f)
1959 if base not in bbfile_seen:
1960 bbfile_seen[base] = f
1961 else:
1962 topfile = bbfile_seen[base]
1963 self.overlayed[topfile].append(f)
1964
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001965 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001966
1967 def get_file_appends(self, fn):
1968 """
1969 Returns a list of .bbappend files to apply to fn
1970 """
1971 filelist = []
1972 f = os.path.basename(fn)
1973 for b in self.bbappends:
1974 (bbappend, filename) = b
1975 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1976 filelist.append(filename)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001977 return tuple(filelist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001978
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001979 def collection_priorities(self, pkgfns, fns, d):
1980 # Return the priorities of the entries in pkgfns
1981 # Also check that all the regexes in self.bbfile_config_priorities are used
1982 # (but to do that we need to ensure skipped recipes aren't counted, nor
1983 # collections in BBFILE_PATTERN_IGNORE_EMPTY)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001984
1985 priorities = {}
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001986 seen = set()
1987 matched = set()
1988
1989 matched_regex = set()
1990 unmatched_regex = set()
1991 for _, _, regex, _ in self.bbfile_config_priorities:
1992 unmatched_regex.add(regex)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001993
1994 # Calculate priorities for each file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001995 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001996 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001997 priorities[p], regex = self.calc_bbfile_priority(realfn)
1998 if regex in unmatched_regex:
1999 matched_regex.add(regex)
2000 unmatched_regex.remove(regex)
2001 seen.add(realfn)
2002 if regex:
2003 matched.add(realfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002004
Andrew Geisslerb7d28612020-07-24 16:15:54 -05002005 if unmatched_regex:
2006 # Account for bbappend files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002007 for b in self.bbappends:
2008 (bbfile, append) = b
Andrew Geisslerb7d28612020-07-24 16:15:54 -05002009 seen.add(append)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002010
Andrew Geisslerb7d28612020-07-24 16:15:54 -05002011 # Account for skipped recipes
2012 seen.update(fns)
2013
2014 seen.difference_update(matched)
2015
2016 def already_matched(fn):
2017 for regex in matched_regex:
2018 if regex.match(fn):
2019 return True
2020 return False
2021
2022 for unmatch in unmatched_regex.copy():
2023 for fn in seen:
2024 if unmatch.match(fn):
2025 # If the bbappend or file was already matched by another regex, skip it
2026 # e.g. for a layer within a layer, the outer regex could match, the inner
2027 # regex may match nothing and we should warn about that
2028 if already_matched(fn):
2029 continue
2030 unmatched_regex.remove(unmatch)
2031 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002032
2033 for collection, pattern, regex, _ in self.bbfile_config_priorities:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05002034 if regex in unmatched_regex:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002035 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Andrew Geissler5a43b432020-06-13 10:46:56 -05002036 collectlog.warning("No bb files in %s matched BBFILE_PATTERN_%s '%s'" % (self.mc if self.mc else 'default',
2037 collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002038
2039 return priorities
2040
2041class ParsingFailure(Exception):
2042 def __init__(self, realexception, recipe):
2043 self.realexception = realexception
2044 self.recipe = recipe
2045 Exception.__init__(self, realexception, recipe)
2046
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002047class Parser(multiprocessing.Process):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002048 def __init__(self, jobs, results, quit, profile):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002049 self.jobs = jobs
2050 self.results = results
2051 self.quit = quit
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002052 multiprocessing.Process.__init__(self)
2053 self.context = bb.utils.get_context().copy()
2054 self.handlers = bb.event.get_class_handlers().copy()
2055 self.profile = profile
Andrew Geissler9aee5002022-03-30 16:27:02 +00002056 self.queue_signals = False
2057 self.signal_received = []
2058 self.signal_threadlock = threading.Lock()
2059
2060 def catch_sig(self, signum, frame):
2061 if self.queue_signals:
2062 self.signal_received.append(signum)
2063 else:
2064 self.handle_sig(signum, frame)
2065
2066 def handle_sig(self, signum, frame):
2067 if signum == signal.SIGTERM:
2068 signal.signal(signal.SIGTERM, signal.SIG_DFL)
2069 os.kill(os.getpid(), signal.SIGTERM)
2070 elif signum == signal.SIGINT:
2071 signal.default_int_handler(signum, frame)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002072
2073 def run(self):
2074
2075 if not self.profile:
2076 self.realrun()
2077 return
2078
2079 try:
2080 import cProfile as profile
2081 except:
2082 import profile
2083 prof = profile.Profile()
2084 try:
2085 profile.Profile.runcall(prof, self.realrun)
2086 finally:
2087 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
2088 prof.dump_stats(logfile)
2089
2090 def realrun(self):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002091 # Signal handling here is hard. We must not terminate any process or thread holding the write
2092 # lock for the event stream as it will not be released, ever, and things will hang.
2093 # Python handles signals in the main thread/process but they can be raised from any thread and
2094 # we want to defer processing of any SIGTERM/SIGINT signal until we're outside the critical section
2095 # and don't hold the lock (see server/process.py). We therefore always catch the signals (so any
2096 # new thread should also do so) and we defer handling but we handle with the local thread lock
2097 # held (a threading lock, not a multiprocessing one) so that no other thread in the process
2098 # can be in the critical section.
2099 signal.signal(signal.SIGTERM, self.catch_sig)
2100 signal.signal(signal.SIGHUP, signal.SIG_DFL)
2101 signal.signal(signal.SIGINT, self.catch_sig)
2102 bb.utils.set_process_name(multiprocessing.current_process().name)
2103 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2104 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002105
2106 pending = []
Andrew Geissler517393d2023-01-13 08:55:19 -06002107 havejobs = True
Andrew Geissler9aee5002022-03-30 16:27:02 +00002108 try:
Andrew Geissler517393d2023-01-13 08:55:19 -06002109 while havejobs or pending:
2110 if self.quit.is_set():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002111 break
Andrew Geissler9aee5002022-03-30 16:27:02 +00002112
Andrew Geissler517393d2023-01-13 08:55:19 -06002113 job = None
2114 try:
2115 job = self.jobs.pop()
2116 except IndexError:
2117 havejobs = False
2118 if job:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002119 result = self.parse(*job)
2120 # Clear the siggen cache after parsing to control memory usage, its huge
2121 bb.parse.siggen.postparsing_clean_cache()
Andrew Geissler9aee5002022-03-30 16:27:02 +00002122 pending.append(result)
Andrew Geissler517393d2023-01-13 08:55:19 -06002123
2124 if pending:
2125 try:
2126 result = pending.pop()
2127 self.results.put(result, timeout=0.05)
2128 except queue.Full:
2129 pending.append(result)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002130 finally:
2131 self.results.close()
2132 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002133
Andrew Geissler5a43b432020-06-13 10:46:56 -05002134 def parse(self, mc, cache, filename, appends):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002135 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002136 origfilter = bb.event.LogHandler.filter
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002137 # Record the filename we're parsing into any events generated
2138 def parse_filter(self, record):
2139 record.taskpid = bb.event.worker_pid
2140 record.fn = filename
2141 return True
2142
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002143 # Reset our environment and handlers to the original settings
2144 bb.utils.set_context(self.context.copy())
2145 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002146 bb.event.LogHandler.filter = parse_filter
2147
Andrew Geissler5a43b432020-06-13 10:46:56 -05002148 return True, mc, cache.parse(filename, appends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002149 except Exception as exc:
2150 tb = sys.exc_info()[2]
2151 exc.recipe = filename
2152 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002153 return True, None, exc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002154 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
2155 # and for example a worker thread doesn't just exit on its own in response to
2156 # a SystemExit event for example.
2157 except BaseException as exc:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002158 return True, None, ParsingFailure(exc, filename)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002159 finally:
2160 bb.event.LogHandler.filter = origfilter
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002161
2162class CookerParser(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002163 def __init__(self, cooker, mcfilelist, masked):
2164 self.mcfilelist = mcfilelist
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002165 self.cooker = cooker
2166 self.cfgdata = cooker.data
2167 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002168 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002169
2170 # Accounting statistics
2171 self.parsed = 0
2172 self.cached = 0
2173 self.error = 0
2174 self.masked = masked
2175
2176 self.skipped = 0
2177 self.virtuals = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002178
2179 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002180 self.process_names = []
2181
Andrew Geissler5a43b432020-06-13 10:46:56 -05002182 self.bb_caches = bb.cache.MulticonfigCache(self.cfgbuilder, self.cfghash, cooker.caches_array)
2183 self.fromcache = set()
2184 self.willparse = set()
2185 for mc in self.cooker.multiconfigs:
2186 for filename in self.mcfilelist[mc]:
2187 appends = self.cooker.collections[mc].get_file_appends(filename)
2188 if not self.bb_caches[mc].cacheValid(filename, appends):
2189 self.willparse.add((mc, self.bb_caches[mc], filename, appends))
2190 else:
2191 self.fromcache.add((mc, self.bb_caches[mc], filename, appends))
2192
2193 self.total = len(self.fromcache) + len(self.willparse)
2194 self.toparse = len(self.willparse)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002195 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002196
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002197 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Andrew Geissler5a43b432020-06-13 10:46:56 -05002198 multiprocessing.cpu_count()), self.toparse)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002199
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002200 bb.cache.SiggenRecipeInfo.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002201 self.start()
2202 self.haveshutdown = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002203 self.syncthread = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002204
2205 def start(self):
2206 self.results = self.load_cached()
2207 self.processes = []
2208 if self.toparse:
2209 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002210
Andrew Geissler517393d2023-01-13 08:55:19 -06002211 self.parser_quit = multiprocessing.Event()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002212 self.result_queue = multiprocessing.Queue()
Brad Bishop19323692019-04-05 15:28:33 -04002213
2214 def chunkify(lst,n):
2215 return [lst[i::n] for i in range(n)]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002216 self.jobs = chunkify(list(self.willparse), self.num_processes)
Brad Bishop19323692019-04-05 15:28:33 -04002217
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002218 for i in range(0, self.num_processes):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002219 parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, self.cooker.configuration.profile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002220 parser.start()
2221 self.process_names.append(parser.name)
2222 self.processes.append(parser)
2223
2224 self.results = itertools.chain(self.results, self.parse_generator())
2225
Andrew Geissler9aee5002022-03-30 16:27:02 +00002226 def shutdown(self, clean=True):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002227 if not self.toparse:
2228 return
2229 if self.haveshutdown:
2230 return
2231 self.haveshutdown = True
2232
2233 if clean:
2234 event = bb.event.ParseCompleted(self.cached, self.parsed,
2235 self.skipped, self.masked,
2236 self.virtuals, self.error,
2237 self.total)
2238
2239 bb.event.fire(event, self.cfgdata)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002240 else:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002241 bb.error("Parsing halted due to errors, see error messages above")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002242
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002243 # Cleanup the queue before call process.join(), otherwise there might be
2244 # deadlocks.
2245 while True:
2246 try:
2247 self.result_queue.get(timeout=0.25)
2248 except queue.Empty:
2249 break
2250
Andrew Geissler517393d2023-01-13 08:55:19 -06002251 def sync_caches():
2252 for c in self.bb_caches.values():
2253 bb.cache.SiggenRecipeInfo.reset()
2254 c.sync()
2255
2256 self.syncthread = threading.Thread(target=sync_caches, name="SyncThread")
2257 self.syncthread.start()
2258
2259 self.parser_quit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002260
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002261 for process in self.processes:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002262 process.join(0.5)
2263
2264 for process in self.processes:
2265 if process.exitcode is None:
2266 os.kill(process.pid, signal.SIGINT)
2267
2268 for process in self.processes:
2269 process.join(0.5)
2270
2271 for process in self.processes:
2272 if process.exitcode is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002273 process.terminate()
Andrew Geissler9aee5002022-03-30 16:27:02 +00002274
2275 for process in self.processes:
2276 process.join()
2277 # Added in 3.7, cleans up zombies
2278 if hasattr(process, "close"):
2279 process.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002280
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002281 bb.codeparser.parser_cache_save()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002282 bb.codeparser.parser_cache_savemerge()
Andrew Geissler517393d2023-01-13 08:55:19 -06002283 bb.cache.SiggenRecipeInfo.reset()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002284 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002285 if self.cooker.configuration.profile:
2286 profiles = []
2287 for i in self.process_names:
2288 logfile = "profile-parse-%s.log" % i
2289 if os.path.exists(logfile):
2290 profiles.append(logfile)
2291
2292 pout = "profile-parse.log.processed"
2293 bb.utils.process_profilelog(profiles, pout = pout)
2294 print("Processed parsing statistics saved to %s" % (pout))
2295
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002296 def final_cleanup(self):
2297 if self.syncthread:
2298 self.syncthread.join()
2299
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002300 def load_cached(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002301 for mc, cache, filename, appends in self.fromcache:
Andrew Geissler517393d2023-01-13 08:55:19 -06002302 infos = cache.loadCached(filename, appends)
2303 yield False, mc, infos
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002304
2305 def parse_generator(self):
Andrew Geissler595f6302022-01-24 19:11:47 +00002306 empty = False
2307 while self.processes or not empty:
2308 for process in self.processes.copy():
2309 if not process.is_alive():
2310 process.join()
2311 self.processes.remove(process)
2312
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002313 if self.parsed >= self.toparse:
2314 break
2315
2316 try:
2317 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002318 except queue.Empty:
Andrew Geissler595f6302022-01-24 19:11:47 +00002319 empty = True
Andrew Geissler9aee5002022-03-30 16:27:02 +00002320 yield None, None, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002321 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002322 empty = False
Andrew Geissler9aee5002022-03-30 16:27:02 +00002323 yield result
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002324
Andrew Geissler595f6302022-01-24 19:11:47 +00002325 if not (self.parsed >= self.toparse):
2326 raise bb.parse.ParseError("Not all recipes parsed, parser thread killed/died? Exiting.", None)
2327
2328
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002329 def parse_next(self):
2330 result = []
2331 parsed = None
2332 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002333 parsed, mc, result = next(self.results)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002334 if isinstance(result, BaseException):
2335 # Turn exceptions back into exceptions
2336 raise result
2337 if parsed is None:
2338 # Timeout, loop back through the main loop
2339 return True
2340
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002341 except StopIteration:
2342 self.shutdown()
2343 return False
2344 except bb.BBHandledException as exc:
2345 self.error += 1
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002346 logger.debug('Failed to parse recipe: %s' % exc.recipe)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002347 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002348 return False
2349 except ParsingFailure as exc:
2350 self.error += 1
2351 logger.error('Unable to parse %s: %s' %
2352 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002353 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002354 return False
2355 except bb.parse.ParseError as exc:
2356 self.error += 1
2357 logger.error(str(exc))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002358 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002359 return False
2360 except bb.data_smart.ExpansionError as exc:
2361 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002362 bbdir = os.path.dirname(__file__) + os.sep
2363 etype, value, _ = sys.exc_info()
2364 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2365 logger.error('ExpansionError during parsing %s', value.recipe,
2366 exc_info=(etype, value, tb))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002367 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002368 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002369 except Exception as exc:
2370 self.error += 1
2371 etype, value, tb = sys.exc_info()
2372 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002373 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002374 exc_info=(etype, value, exc.traceback))
2375 else:
2376 # Most likely, an exception occurred during raising an exception
2377 import traceback
2378 logger.error('Exception during parse: %s' % traceback.format_exc())
Andrew Geissler9aee5002022-03-30 16:27:02 +00002379 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002380 return False
2381
2382 self.current += 1
2383 self.virtuals += len(result)
2384 if parsed:
2385 self.parsed += 1
2386 if self.parsed % self.progress_chunk == 0:
2387 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2388 self.cfgdata)
2389 else:
2390 self.cached += 1
2391
2392 for virtualfn, info_array in result:
2393 if info_array[0].skipped:
2394 self.skipped += 1
2395 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Andrew Geissler5a43b432020-06-13 10:46:56 -05002396 self.bb_caches[mc].add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002397 parsed=parsed, watcher = self.cooker.add_filewatch)
2398 return True
2399
2400 def reparse(self, filename):
Andrew Geissler517393d2023-01-13 08:55:19 -06002401 bb.cache.SiggenRecipeInfo.reset()
Andrew Geissler5a43b432020-06-13 10:46:56 -05002402 to_reparse = set()
2403 for mc in self.cooker.multiconfigs:
2404 to_reparse.add((mc, filename, self.cooker.collections[mc].get_file_appends(filename)))
2405
2406 for mc, filename, appends in to_reparse:
2407 infos = self.bb_caches[mc].parse(filename, appends)
2408 for vfn, info_array in infos:
2409 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)