blob: 9123605461ede05bfe9f66e59865776980d574a8 [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
16import sre_constants
17import threading
Patrick Williamsc0f7c042017-02-23 20:41:17 -060018from io import StringIO, UnsupportedOperation
Patrick Williamsc124f4f2015-09-15 14:41:29 -050019from contextlib import closing
Patrick Williamsc0f7c042017-02-23 20:41:17 -060020from collections import defaultdict, namedtuple
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021import bb, bb.exceptions, bb.command
22from bb import utils, data, parse, event, cache, providers, taskdata, runqueue, build
Patrick Williamsc0f7c042017-02-23 20:41:17 -060023import queue
Patrick Williamsc124f4f2015-09-15 14:41:29 -050024import signal
Patrick Williamsc124f4f2015-09-15 14:41:29 -050025import prserv.serv
26import pyinotify
Patrick Williamsc0f7c042017-02-23 20:41:17 -060027import json
28import pickle
29import codecs
Brad Bishop08902b02019-08-20 09:16:51 -040030import hashserv
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031
32logger = logging.getLogger("BitBake")
33collectlog = logging.getLogger("BitBake.Collection")
34buildlog = logging.getLogger("BitBake.Build")
35parselog = logging.getLogger("BitBake.Parsing")
36providerlog = logging.getLogger("BitBake.Provider")
37
38class NoSpecificMatch(bb.BBHandledException):
39 """
40 Exception raised when no or multiple file matches are found
41 """
42
43class NothingToBuild(Exception):
44 """
45 Exception raised when there is nothing to build
46 """
47
48class CollectionError(bb.BBHandledException):
49 """
50 Exception raised when layer configuration is incorrect
51 """
52
53class state:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060054 initial, parsing, running, shutdown, forceshutdown, stopped, error = list(range(7))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050055
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050056 @classmethod
57 def get_name(cls, code):
58 for name in dir(cls):
59 value = getattr(cls, name)
60 if type(value) == type(cls.initial) and value == code:
61 return name
62 raise ValueError("Invalid status code: %s" % code)
63
Patrick Williamsc124f4f2015-09-15 14:41:29 -050064
65class SkippedPackage:
66 def __init__(self, info = None, reason = None):
67 self.pn = None
68 self.skipreason = None
69 self.provides = None
70 self.rprovides = None
71
72 if info:
73 self.pn = info.pn
74 self.skipreason = info.skipreason
75 self.provides = info.provides
76 self.rprovides = info.rprovides
77 elif reason:
78 self.skipreason = reason
79
80
81class CookerFeatures(object):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060082 _feature_list = [HOB_EXTRA_CACHES, BASEDATASTORE_TRACKING, SEND_SANITYEVENTS] = list(range(3))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050083
84 def __init__(self):
85 self._features=set()
86
87 def setFeature(self, f):
88 # validate we got a request for a feature we support
89 if f not in CookerFeatures._feature_list:
90 return
91 self._features.add(f)
92
93 def __contains__(self, f):
94 return f in self._features
95
96 def __iter__(self):
97 return self._features.__iter__()
98
Patrick Williamsc0f7c042017-02-23 20:41:17 -060099 def __next__(self):
100 return next(self._features)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500101
102
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600103class EventWriter:
104 def __init__(self, cooker, eventfile):
105 self.file_inited = None
106 self.cooker = cooker
107 self.eventfile = eventfile
108 self.event_queue = []
109
110 def write_event(self, event):
111 with open(self.eventfile, "a") as f:
112 try:
113 str_event = codecs.encode(pickle.dumps(event), 'base64').decode('utf-8')
114 f.write("%s\n" % json.dumps({"class": event.__module__ + "." + event.__class__.__name__,
115 "vars": str_event}))
116 except Exception as err:
117 import traceback
118 print(err, traceback.format_exc())
119
120 def send(self, event):
121 if self.file_inited:
122 # we have the file, just write the event
123 self.write_event(event)
124 else:
125 # init on bb.event.BuildStarted
126 name = "%s.%s" % (event.__module__, event.__class__.__name__)
127 if name in ("bb.event.BuildStarted", "bb.cooker.CookerExit"):
128 with open(self.eventfile, "w") as f:
129 f.write("%s\n" % json.dumps({ "allvariables" : self.cooker.getAllKeysWithFlags(["doc", "func"])}))
130
131 self.file_inited = True
132
133 # write pending events
134 for evt in self.event_queue:
135 self.write_event(evt)
136
137 # also write the current event
138 self.write_event(event)
139 else:
140 # queue all events until the file is inited
141 self.event_queue.append(event)
142
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500143#============================================================================#
144# BBCooker
145#============================================================================#
146class BBCooker:
147 """
148 Manages one bitbake build run
149 """
150
Andrew Geissler635e0e42020-08-21 15:58:33 -0500151 def __init__(self, configuration, featureSet=None, idleCallBackRegister=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600152 self.recipecaches = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500153 self.skiplist = {}
154 self.featureset = CookerFeatures()
155 if featureSet:
156 for f in featureSet:
157 self.featureset.setFeature(f)
158
159 self.configuration = configuration
160
Andrew Geissler635e0e42020-08-21 15:58:33 -0500161 self.idleCallBackRegister = idleCallBackRegister
162
Brad Bishopf058f492019-01-28 23:50:33 -0500163 bb.debug(1, "BBCooker starting %s" % time.time())
164 sys.stdout.flush()
165
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500166 self.configwatcher = pyinotify.WatchManager()
Brad Bishopf058f492019-01-28 23:50:33 -0500167 bb.debug(1, "BBCooker pyinotify1 %s" % time.time())
168 sys.stdout.flush()
169
Andrew Geissler82c905d2020-04-13 13:39:40 -0500170 self.configwatcher.bbseen = set()
171 self.configwatcher.bbwatchedfiles = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500172 self.confignotifier = pyinotify.Notifier(self.configwatcher, self.config_notifications)
Brad Bishopf058f492019-01-28 23:50:33 -0500173 bb.debug(1, "BBCooker pyinotify2 %s" % time.time())
174 sys.stdout.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500175 self.watchmask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CREATE | pyinotify.IN_DELETE | \
176 pyinotify.IN_DELETE_SELF | pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF | \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500177 pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500178 self.watcher = pyinotify.WatchManager()
Brad Bishopf058f492019-01-28 23:50:33 -0500179 bb.debug(1, "BBCooker pyinotify3 %s" % time.time())
180 sys.stdout.flush()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500181 self.watcher.bbseen = set()
182 self.watcher.bbwatchedfiles = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500183 self.notifier = pyinotify.Notifier(self.watcher, self.notifications)
184
Brad Bishopf058f492019-01-28 23:50:33 -0500185 bb.debug(1, "BBCooker pyinotify complete %s" % time.time())
186 sys.stdout.flush()
187
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500188 # If being called by something like tinfoil, we need to clean cached data
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500189 # which may now be invalid
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500190 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500191 bb.parse.BBHandler.cached_statements = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500192
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500193 self.ui_cmdline = None
Brad Bishop08902b02019-08-20 09:16:51 -0400194 self.hashserv = None
Brad Bishopa34c0302019-09-23 22:34:48 -0400195 self.hashservaddr = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500196
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500197 self.initConfigurationData()
198
Brad Bishopf058f492019-01-28 23:50:33 -0500199 bb.debug(1, "BBCooker parsed base configuration %s" % time.time())
200 sys.stdout.flush()
201
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600202 # we log all events to a file if so directed
203 if self.configuration.writeeventlog:
204 # register the log file writer as UI Handler
205 writer = EventWriter(self, self.configuration.writeeventlog)
206 EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
207 bb.event.register_UIHhandler(EventLogWriteHandler(writer))
208
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500209 self.inotify_modified_files = []
210
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500211 def _process_inotify_updates(server, cooker, abort):
212 cooker.process_inotify_updates()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500213 return 1.0
214
Andrew Geissler635e0e42020-08-21 15:58:33 -0500215 self.idleCallBackRegister(_process_inotify_updates, self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500216
217 # TOSTOP must not be set or our children will hang when they output
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600218 try:
219 fd = sys.stdout.fileno()
220 if os.isatty(fd):
221 import termios
222 tcattr = termios.tcgetattr(fd)
223 if tcattr[3] & termios.TOSTOP:
224 buildlog.info("The terminal had the TOSTOP bit set, clearing...")
225 tcattr[3] = tcattr[3] & ~termios.TOSTOP
226 termios.tcsetattr(fd, termios.TCSANOW, tcattr)
227 except UnsupportedOperation:
228 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500229
230 self.command = bb.command.Command(self)
231 self.state = state.initial
232
233 self.parser = None
234
235 signal.signal(signal.SIGTERM, self.sigterm_exception)
236 # Let SIGHUP exit as SIGTERM
237 signal.signal(signal.SIGHUP, self.sigterm_exception)
238
Brad Bishopf058f492019-01-28 23:50:33 -0500239 bb.debug(1, "BBCooker startup complete %s" % time.time())
240 sys.stdout.flush()
241
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500242 def process_inotify_updates(self):
243 for n in [self.confignotifier, self.notifier]:
244 if n.check_events(timeout=0):
245 # read notified events and enqeue them
246 n.read_events()
247 n.process_events()
248
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500249 def config_notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500250 if event.maskname == "IN_Q_OVERFLOW":
251 bb.warn("inotify event queue overflowed, invalidating caches.")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500252 self.parsecache_valid = False
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500253 self.baseconfig_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500254 bb.parse.clear_cache()
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500255 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500256 if not event.pathname in self.configwatcher.bbwatchedfiles:
257 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500258 if not event.pathname in self.inotify_modified_files:
259 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500260 self.baseconfig_valid = False
261
262 def notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500263 if event.maskname == "IN_Q_OVERFLOW":
264 bb.warn("inotify event queue overflowed, invalidating caches.")
265 self.parsecache_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500266 bb.parse.clear_cache()
267 return
268 if event.pathname.endswith("bitbake-cookerdaemon.log") \
269 or event.pathname.endswith("bitbake.lock"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500270 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500271 if not event.pathname in self.inotify_modified_files:
272 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500273 self.parsecache_valid = False
274
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500275 def add_filewatch(self, deps, watcher=None, dirs=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500276 if not watcher:
277 watcher = self.watcher
278 for i in deps:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500279 watcher.bbwatchedfiles.add(i[0])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500280 if dirs:
281 f = i[0]
282 else:
283 f = os.path.dirname(i[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500284 if f in watcher.bbseen:
285 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -0500286 watcher.bbseen.add(f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500287 watchtarget = None
288 while True:
289 # We try and add watches for files that don't exist but if they did, would influence
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500290 # the parser. The parent directory of these files may not exist, in which case we need
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500291 # to watch any parent that does exist for changes.
292 try:
293 watcher.add_watch(f, self.watchmask, quiet=False)
294 if watchtarget:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500295 watcher.bbwatchedfiles.add(watchtarget)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500296 break
297 except pyinotify.WatchManagerError as e:
298 if 'ENOENT' in str(e):
299 watchtarget = f
300 f = os.path.dirname(f)
301 if f in watcher.bbseen:
302 break
Andrew Geissler82c905d2020-04-13 13:39:40 -0500303 watcher.bbseen.add(f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500304 continue
305 if 'ENOSPC' in str(e):
306 providerlog.error("No space left on device or exceeds fs.inotify.max_user_watches?")
307 providerlog.error("To check max_user_watches: sysctl -n fs.inotify.max_user_watches.")
308 providerlog.error("To modify max_user_watches: sysctl -n -w fs.inotify.max_user_watches=<value>.")
309 providerlog.error("Root privilege is required to modify max_user_watches.")
310 raise
311
312 def sigterm_exception(self, signum, stackframe):
313 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500314 bb.warn("Cooker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500315 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500316 bb.warn("Cooker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317 self.state = state.forceshutdown
318
319 def setFeatures(self, features):
320 # we only accept a new feature set if we're in state initial, so we can reset without problems
321 if not self.state in [state.initial, state.shutdown, state.forceshutdown, state.stopped, state.error]:
322 raise Exception("Illegal state for feature set change")
323 original_featureset = list(self.featureset)
324 for feature in features:
325 self.featureset.setFeature(feature)
326 bb.debug(1, "Features set %s (was %s)" % (original_featureset, list(self.featureset)))
327 if (original_featureset != list(self.featureset)) and self.state != state.error:
328 self.reset()
329
330 def initConfigurationData(self):
331
332 self.state = state.initial
333 self.caches_array = []
334
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500335 # Need to preserve BB_CONSOLELOG over resets
336 consolelog = None
337 if hasattr(self, "data"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500338 consolelog = self.data.getVar("BB_CONSOLELOG")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500339
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500340 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
341 self.enableDataTracking()
342
343 all_extra_cache_names = []
344 # We hardcode all known cache types in a single place, here.
345 if CookerFeatures.HOB_EXTRA_CACHES in self.featureset:
346 all_extra_cache_names.append("bb.cache_extra:HobRecipeInfo")
347
348 caches_name_array = ['bb.cache:CoreRecipeInfo'] + all_extra_cache_names
349
350 # At least CoreRecipeInfo will be loaded, so caches_array will never be empty!
351 # This is the entry point, no further check needed!
352 for var in caches_name_array:
353 try:
354 module_name, cache_name = var.split(':')
355 module = __import__(module_name, fromlist=(cache_name,))
356 self.caches_array.append(getattr(module, cache_name))
357 except ImportError as exc:
358 logger.critical("Unable to import extra RecipeInfo '%s' from '%s': %s" % (cache_name, module_name, exc))
359 sys.exit("FATAL: Failed to import extra cache class '%s'." % cache_name)
360
361 self.databuilder = bb.cookerdata.CookerDataBuilder(self.configuration, False)
362 self.databuilder.parseBaseConfiguration()
363 self.data = self.databuilder.data
364 self.data_hash = self.databuilder.data_hash
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500365 self.extraconfigdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500366
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500367 if consolelog:
368 self.data.setVar("BB_CONSOLELOG", consolelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500369
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500370 self.data.setVar('BB_CMDLINE', self.ui_cmdline)
371
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500372 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
373 self.disableDataTracking()
374
Brad Bishop15ae2502019-06-18 21:44:24 -0400375 for mc in self.databuilder.mcdata.values():
376 mc.renameVar("__depends", "__base_depends")
377 self.add_filewatch(mc.getVar("__base_depends", False), self.configwatcher)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500378
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500379 self.baseconfig_valid = True
380 self.parsecache_valid = False
381
382 def handlePRServ(self):
383 # Setup a PR Server based on the new configuration
384 try:
385 self.prhost = prserv.serv.auto_start(self.data)
386 except prserv.serv.PRServiceConfigError as e:
387 bb.fatal("Unable to start PR Server, exitting")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500388
Brad Bishopa34c0302019-09-23 22:34:48 -0400389 if self.data.getVar("BB_HASHSERVE") == "auto":
390 # Create a new hash server bound to a unix domain socket
Brad Bishop08902b02019-08-20 09:16:51 -0400391 if not self.hashserv:
392 dbfile = (self.data.getVar("PERSISTENT_DIR") or self.data.getVar("CACHE")) + "/hashserv.db"
Brad Bishopa34c0302019-09-23 22:34:48 -0400393 self.hashservaddr = "unix://%s/hashserve.sock" % self.data.getVar("TOPDIR")
394 self.hashserv = hashserv.create_server(self.hashservaddr, dbfile, sync=False)
Brad Bishop08902b02019-08-20 09:16:51 -0400395 self.hashserv.process = multiprocessing.Process(target=self.hashserv.serve_forever)
Brad Bishop08902b02019-08-20 09:16:51 -0400396 self.hashserv.process.start()
Brad Bishopa34c0302019-09-23 22:34:48 -0400397 self.data.setVar("BB_HASHSERVE", self.hashservaddr)
398 self.databuilder.origdata.setVar("BB_HASHSERVE", self.hashservaddr)
399 self.databuilder.data.setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400400 for mc in self.databuilder.mcdata:
Brad Bishopa34c0302019-09-23 22:34:48 -0400401 self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400402
403 bb.parse.init_parser(self.data)
404
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500405 def enableDataTracking(self):
406 self.configuration.tracking = True
407 if hasattr(self, "data"):
408 self.data.enableTracking()
409
410 def disableDataTracking(self):
411 self.configuration.tracking = False
412 if hasattr(self, "data"):
413 self.data.disableTracking()
414
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500415 def parseConfiguration(self):
416 # Set log file verbosity
417 verboselogs = bb.utils.to_boolean(self.data.getVar("BB_VERBOSE_LOGS", False))
418 if verboselogs:
419 bb.msg.loggerVerboseLogs = True
420
421 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500422 nice = self.data.getVar("BB_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500423 if nice:
424 curnice = os.nice(0)
425 nice = int(nice) - curnice
426 buildlog.verbose("Renice to %s " % os.nice(nice))
427
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600428 if self.recipecaches:
429 del self.recipecaches
430 self.multiconfigs = self.databuilder.mcdata.keys()
431 self.recipecaches = {}
432 for mc in self.multiconfigs:
433 self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500434
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500435 self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500436
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500437 self.parsecache_valid = False
438
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500439 def updateConfigOpts(self, options, environment, cmdline):
440 self.ui_cmdline = cmdline
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500441 clean = True
442 for o in options:
443 if o in ['prefile', 'postfile']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500444 # Only these options may require a reparse
445 try:
446 if getattr(self.configuration, o) == options[o]:
447 # Value is the same, no need to mark dirty
448 continue
449 except AttributeError:
450 pass
451 logger.debug(1, "Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
452 print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500453 clean = False
454 setattr(self.configuration, o, options[o])
455 for k in bb.utils.approved_variables():
456 if k in environment and k not in self.configuration.env:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500457 logger.debug(1, "Updating new environment variable %s to %s" % (k, environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500458 self.configuration.env[k] = environment[k]
459 clean = False
460 if k in self.configuration.env and k not in environment:
461 logger.debug(1, "Updating environment variable %s (deleted)" % (k))
462 del self.configuration.env[k]
463 clean = False
464 if k not in self.configuration.env and k not in environment:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500465 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500466 if environment[k] != self.configuration.env[k]:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500467 logger.debug(1, "Updating environment variable %s from %s to %s" % (k, self.configuration.env[k], environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500468 self.configuration.env[k] = environment[k]
469 clean = False
470 if not clean:
471 logger.debug(1, "Base environment change, triggering reparse")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500472 self.reset()
473
474 def runCommands(self, server, data, abort):
475 """
476 Run any queued asynchronous command
477 This is done by the idle handler so it runs in true context rather than
478 tied to any UI.
479 """
480
481 return self.command.runAsyncCommand()
482
483 def showVersions(self):
484
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500485 (latest_versions, preferred_versions) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500486
487 logger.plain("%-35s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version")
488 logger.plain("%-35s %25s %25s\n", "===========", "==============", "=================")
489
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500490 for p in sorted(self.recipecaches[''].pkg_pn):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491 pref = preferred_versions[p]
492 latest = latest_versions[p]
493
494 prefstr = pref[0][0] + ":" + pref[0][1] + '-' + pref[0][2]
495 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
496
497 if pref == latest:
498 prefstr = ""
499
500 logger.plain("%-35s %25s %25s", p, lateststr, prefstr)
501
502 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
503 """
504 Show the outer or per-recipe environment
505 """
506 fn = None
507 envdata = None
Brad Bishop15ae2502019-06-18 21:44:24 -0400508 mc = ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500509 if not pkgs_to_build:
510 pkgs_to_build = []
511
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500512 orig_tracking = self.configuration.tracking
513 if not orig_tracking:
514 self.enableDataTracking()
515 self.reset()
516
Brad Bishop15ae2502019-06-18 21:44:24 -0400517 def mc_base(p):
518 if p.startswith('mc:'):
519 s = p.split(':')
520 if len(s) == 2:
521 return s[1]
522 return None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500523
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500524 if buildfile:
525 # Parse the configuration here. We need to do it explicitly here since
526 # this showEnvironment() code path doesn't use the cache
527 self.parseConfiguration()
528
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600529 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500530 fn = self.matchFile(fn, mc)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600531 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500532 elif len(pkgs_to_build) == 1:
Brad Bishop15ae2502019-06-18 21:44:24 -0400533 mc = mc_base(pkgs_to_build[0])
534 if not mc:
535 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
536 if pkgs_to_build[0] in set(ignore.split()):
537 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500538
Brad Bishop15ae2502019-06-18 21:44:24 -0400539 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.abort, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500540
Brad Bishop15ae2502019-06-18 21:44:24 -0400541 mc = runlist[0][0]
542 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500543
544 if fn:
545 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -0500546 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
547 envdata = bb_caches[mc].loadDataFull(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500548 except Exception as e:
549 parselog.exception("Unable to read %s", fn)
550 raise
Brad Bishop15ae2502019-06-18 21:44:24 -0400551 else:
552 if not mc in self.databuilder.mcdata:
553 bb.fatal('Not multiconfig named "%s" found' % mc)
554 envdata = self.databuilder.mcdata[mc]
555 data.expandKeys(envdata)
556 parse.ast.runAnonFuncs(envdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500557
558 # Display history
559 with closing(StringIO()) as env:
560 self.data.inchistory.emit(env)
561 logger.plain(env.getvalue())
562
563 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500564 with closing(StringIO()) as env:
565 data.emit_env(env, envdata, True)
566 logger.plain(env.getvalue())
567
568 # emit the metadata which isnt valid shell
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500569 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600570 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500571 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500572
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500573 if not orig_tracking:
574 self.disableDataTracking()
575 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500576
577 def buildTaskData(self, pkgs_to_build, task, abort, allowincomplete=False):
578 """
579 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
580 """
581 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
582
583 # A task of None means use the default task
584 if task is None:
585 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500586 if not task.startswith("do_"):
587 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500588
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500589 targetlist = self.checkPackages(pkgs_to_build, task)
590 fulltargetlist = []
591 defaulttask_implicit = ''
592 defaulttask_explicit = False
593 wildcard = False
594
595 # Wild card expansion:
Brad Bishop15ae2502019-06-18 21:44:24 -0400596 # Replace string such as "mc:*:bash"
597 # into "mc:A:bash mc:B:bash bash"
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500598 for k in targetlist:
Brad Bishop15ae2502019-06-18 21:44:24 -0400599 if k.startswith("mc:"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500600 if wildcard:
601 bb.fatal('multiconfig conflict')
602 if k.split(":")[1] == "*":
603 wildcard = True
604 for mc in self.multiconfigs:
605 if mc:
606 fulltargetlist.append(k.replace('*', mc))
607 # implicit default task
608 else:
609 defaulttask_implicit = k.split(":")[2]
610 else:
611 fulltargetlist.append(k)
612 else:
613 defaulttask_explicit = True
614 fulltargetlist.append(k)
615
616 if not defaulttask_explicit and defaulttask_implicit != '':
617 fulltargetlist.append(defaulttask_implicit)
618
619 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600620 taskdata = {}
621 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500622
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600623 for mc in self.multiconfigs:
624 taskdata[mc] = bb.taskdata.TaskData(abort, skiplist=self.skiplist, allowincomplete=allowincomplete)
625 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600626 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627
628 current = 0
629 runlist = []
630 for k in fulltargetlist:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500631 origk = k
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600632 mc = ""
Brad Bishop15ae2502019-06-18 21:44:24 -0400633 if k.startswith("mc:"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600634 mc = k.split(":")[1]
635 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500636 ktask = task
637 if ":do_" in k:
638 k2 = k.split(":do_")
639 k = k2[0]
640 ktask = k2[1]
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500641
642 if mc not in self.multiconfigs:
643 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named %s" % (origk, mc))
644
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600645 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500646 current += 1
647 if not ktask.startswith("do_"):
648 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600649 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
650 # e.g. in ASSUME_PROVIDED
651 continue
652 fn = taskdata[mc].build_targets[k][0]
653 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600655
Brad Bishop15ae2502019-06-18 21:44:24 -0400656 havemc = False
657 for mc in self.multiconfigs:
658 if taskdata[mc].get_mcdepends():
659 havemc = True
Brad Bishopf058f492019-01-28 23:50:33 -0500660
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800661 # No need to do check providers if there are no mcdeps or not an mc build
Brad Bishop15ae2502019-06-18 21:44:24 -0400662 if havemc or len(self.multiconfigs) > 1:
Andrew Geissler99467da2019-02-25 18:54:23 -0600663 seen = set()
664 new = True
665 # Make sure we can provide the multiconfig dependency
666 while new:
667 mcdeps = set()
668 # Add unresolved first, so we can get multiconfig indirect dependencies on time
669 for mc in self.multiconfigs:
670 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
671 mcdeps |= set(taskdata[mc].get_mcdepends())
672 new = False
673 for mc in self.multiconfigs:
674 for k in mcdeps:
675 if k in seen:
676 continue
677 l = k.split(':')
678 depmc = l[2]
679 if depmc not in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500680 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named configuration %s" % (k,depmc))
Andrew Geissler99467da2019-02-25 18:54:23 -0600681 else:
682 logger.debug(1, "Adding providers for multiconfig dependency %s" % l[3])
683 taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
684 seen.add(k)
685 new = True
Brad Bishopf058f492019-01-28 23:50:33 -0500686
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600687 for mc in self.multiconfigs:
688 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
689
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500690 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600691 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500692
693 def prepareTreeData(self, pkgs_to_build, task):
694 """
695 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
696 """
697
698 # We set abort to False here to prevent unbuildable targets raising
699 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600700 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500701
702 return runlist, taskdata
703
704 ######## WARNING : this function requires cache_extra to be enabled ########
705
706 def generateTaskDepTreeData(self, pkgs_to_build, task):
707 """
708 Create a dependency graph of pkgs_to_build including reverse dependency
709 information.
710 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500711 if not task.startswith("do_"):
712 task = "do_%s" % task
713
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500714 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600715 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500716 rq.rqdata.prepare()
717 return self.buildDependTree(rq, taskdata)
718
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600719 @staticmethod
720 def add_mc_prefix(mc, pn):
721 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -0400722 return "mc:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500724
725 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600726 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727 depend_tree = {}
728 depend_tree["depends"] = {}
729 depend_tree["tdepends"] = {}
730 depend_tree["pn"] = {}
731 depend_tree["rdepends-pn"] = {}
732 depend_tree["packages"] = {}
733 depend_tree["rdepends-pkg"] = {}
734 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500735 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600736 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500737
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600738 for mc in taskdata:
739 for name, fn in list(taskdata[mc].get_providermap().items()):
740 pn = self.recipecaches[mc].pkg_fn[fn]
741 pn = self.add_mc_prefix(mc, pn)
742 if name != pn:
743 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
744 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500745
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600746 for tid in rq.rqdata.runtaskentries:
747 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
748 pn = self.recipecaches[mc].pkg_fn[taskfn]
749 pn = self.add_mc_prefix(mc, pn)
750 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500751 if pn not in depend_tree["pn"]:
752 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600753 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500754 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600755 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756
757 # if we have extra caches, list all attributes they bring in
758 extra_info = []
759 for cache_class in self.caches_array:
760 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
761 cachefields = getattr(cache_class, 'cachefields', [])
762 extra_info = extra_info + cachefields
763
764 # for all attributes stored, add them to the dependency tree
765 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600766 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500767
768
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500769 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
770 if not dotname in depend_tree["tdepends"]:
771 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600772 for dep in rq.rqdata.runtaskentries[tid].depends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800773 (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
774 deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600775 depend_tree["tdepends"][dotname].append("%s.%s" % (deppn, bb.runqueue.taskname_from_tid(dep)))
776 if taskfn not in seen_fns:
777 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500778 packages = []
779
780 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600781 for dep in taskdata[mc].depids[taskfn]:
782 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500783
784 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600785 for rdep in taskdata[mc].rdepids[taskfn]:
786 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600788 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500789 for package in rdepends:
790 depend_tree["rdepends-pkg"][package] = []
791 for rdepend in rdepends[package]:
792 depend_tree["rdepends-pkg"][package].append(rdepend)
793 packages.append(package)
794
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600795 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500796 for package in rrecs:
797 depend_tree["rrecs-pkg"][package] = []
798 for rdepend in rrecs[package]:
799 depend_tree["rrecs-pkg"][package].append(rdepend)
800 if not package in packages:
801 packages.append(package)
802
803 for package in packages:
804 if package not in depend_tree["packages"]:
805 depend_tree["packages"][package] = {}
806 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600807 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500808 depend_tree["packages"][package]["version"] = version
809
810 return depend_tree
811
812 ######## WARNING : this function requires cache_extra to be enabled ########
813 def generatePkgDepTreeData(self, pkgs_to_build, task):
814 """
815 Create a dependency tree of pkgs_to_build, returning the data.
816 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500817 if not task.startswith("do_"):
818 task = "do_%s" % task
819
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500820 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600822 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500823 depend_tree = {}
824 depend_tree["depends"] = {}
825 depend_tree["pn"] = {}
826 depend_tree["rdepends-pn"] = {}
827 depend_tree["rdepends-pkg"] = {}
828 depend_tree["rrecs-pkg"] = {}
829
830 # if we have extra caches, list all attributes they bring in
831 extra_info = []
832 for cache_class in self.caches_array:
833 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
834 cachefields = getattr(cache_class, 'cachefields', [])
835 extra_info = extra_info + cachefields
836
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600837 tids = []
838 for mc in taskdata:
839 for tid in taskdata[mc].taskentries:
840 tids.append(tid)
841
842 for tid in tids:
843 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
844
845 pn = self.recipecaches[mc].pkg_fn[taskfn]
846 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500847
848 if pn not in depend_tree["pn"]:
849 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600850 depend_tree["pn"][pn]["filename"] = taskfn
851 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500852 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600853 rdepends = self.recipecaches[mc].rundeps[taskfn]
854 rrecs = self.recipecaches[mc].runrecs[taskfn]
855 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500856
857 # for all extra attributes stored, add them to the dependency tree
858 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600859 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500860
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600861 if taskfn not in seen_fns:
862 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500863
864 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500865 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500866 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600867 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
868 fn_provider = taskdata[mc].build_targets[dep][0]
869 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500870 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500871 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600872 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500873 depend_tree["depends"][pn].append(pn_provider)
874
875 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600876 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500877 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600878 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
879 fn_rprovider = taskdata[mc].run_targets[rdep][0]
880 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500881 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600882 pn_rprovider = rdep
883 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500884 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
885
886 depend_tree["rdepends-pkg"].update(rdepends)
887 depend_tree["rrecs-pkg"].update(rrecs)
888
889 return depend_tree
890
891 def generateDepTreeEvent(self, pkgs_to_build, task):
892 """
893 Create a task dependency graph of pkgs_to_build.
894 Generate an event with the result
895 """
896 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
897 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
898
899 def generateDotGraphFiles(self, pkgs_to_build, task):
900 """
901 Create a task dependency graph of pkgs_to_build.
902 Save the result to a set of .dot files.
903 """
904
905 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
906
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500907 with open('pn-buildlist', 'w') as f:
908 for pn in depgraph["pn"]:
909 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500910 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500911
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500912 # Remove old format output files to ensure no confusion with stale data
913 try:
914 os.unlink('pn-depends.dot')
915 except FileNotFoundError:
916 pass
917 try:
918 os.unlink('package-depends.dot')
919 except FileNotFoundError:
920 pass
Brad Bishop79641f22019-09-10 07:20:22 -0400921 try:
922 os.unlink('recipe-depends.dot')
923 except FileNotFoundError:
924 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500925
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500926 with open('task-depends.dot', 'w') as f:
927 f.write("digraph depends {\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400928 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500929 (pn, taskname) = task.rsplit(".", 1)
930 fn = depgraph["pn"][pn]["filename"]
931 version = depgraph["pn"][pn]["version"]
932 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 -0400933 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500934 f.write('"%s" -> "%s"\n' % (task, dep))
935 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500936 logger.info("Task dependencies saved to 'task-depends.dot'")
937
938 def show_appends_with_no_recipes(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500939 appends_without_recipes = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500940 # Determine which bbappends haven't been applied
Andrew Geissler5a43b432020-06-13 10:46:56 -0500941 for mc in self.multiconfigs:
942 # First get list of recipes, including skipped
943 recipefns = list(self.recipecaches[mc].pkg_fn.keys())
944 recipefns.extend(self.skiplist.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500945
Andrew Geissler5a43b432020-06-13 10:46:56 -0500946 # Work out list of bbappends that have been applied
947 applied_appends = []
948 for fn in recipefns:
949 applied_appends.extend(self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500950
Andrew Geissler5a43b432020-06-13 10:46:56 -0500951 appends_without_recipes[mc] = []
952 for _, appendfn in self.collections[mc].bbappends:
953 if not appendfn in applied_appends:
954 appends_without_recipes[mc].append(appendfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500955
Andrew Geissler5a43b432020-06-13 10:46:56 -0500956 msgs = []
957 for mc in sorted(appends_without_recipes.keys()):
958 if appends_without_recipes[mc]:
959 msgs.append('No recipes in %s available for:\n %s' % (mc if mc else 'default',
960 '\n '.join(appends_without_recipes[mc])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500961
Andrew Geissler5a43b432020-06-13 10:46:56 -0500962 if msgs:
963 msg = "\n".join(msgs)
964 warn_only = self.databuilder.mcdata[mc].getVar("BB_DANGLINGAPPENDS_WARNONLY", \
965 False) or "no"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500966 if warn_only.lower() in ("1", "yes", "true"):
967 bb.warn(msg)
968 else:
969 bb.fatal(msg)
970
971 def handlePrefProviders(self):
972
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600973 for mc in self.multiconfigs:
974 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600975 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500976
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600977 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500978 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600979 try:
980 (providee, provider) = p.split(':')
981 except:
982 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
983 continue
984 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
985 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
986 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500987
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500988 def findConfigFilePath(self, configfile):
989 """
990 Find the location on disk of configfile and if it exists and was parsed by BitBake
991 emit the ConfigFilePathFound event with the path to the file.
992 """
993 path = bb.cookerdata.findConfigFile(configfile, self.data)
994 if not path:
995 return
996
997 # Generate a list of parsed configuration files by searching the files
998 # listed in the __depends and __base_depends variables with a .conf suffix.
999 conffiles = []
1000 dep_files = self.data.getVar('__base_depends', False) or []
1001 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
1002
1003 for f in dep_files:
1004 if f[0].endswith(".conf"):
1005 conffiles.append(f[0])
1006
1007 _, conf, conffile = path.rpartition("conf/")
1008 match = os.path.join(conf, conffile)
1009 # Try and find matches for conf/conffilename.conf as we don't always
1010 # have the full path to the file.
1011 for cfg in conffiles:
1012 if cfg.endswith(match):
1013 bb.event.fire(bb.event.ConfigFilePathFound(path),
1014 self.data)
1015 break
1016
1017 def findFilesMatchingInDir(self, filepattern, directory):
1018 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001019 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001020 'directory' in each BBPATH. i.e. to find all rootfs package classes available
1021 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
1022 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001023 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001024 """
1025
1026 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001027 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001028 for path in bbpaths:
1029 dirpath = os.path.join(path, directory)
1030 if os.path.exists(dirpath):
1031 for root, dirs, files in os.walk(dirpath):
1032 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001033 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001034 matches.append(f)
1035
1036 if matches:
1037 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1038
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001039 def findProviders(self, mc=''):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001040 return bb.providers.findProviders(self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001041
1042 def findBestProvider(self, pn, mc=''):
1043 if pn in self.recipecaches[mc].providers:
1044 filenames = self.recipecaches[mc].providers[pn]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001045 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.databuilder.mcdata[mc], self.recipecaches[mc])
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001046 filename = eligible[0]
1047 return None, None, None, filename
1048 elif pn in self.recipecaches[mc].pkg_pn:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001049 return bb.providers.findBestProvider(pn, self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001050 else:
1051 return None, None, None, None
1052
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001053 def findConfigFiles(self, varname):
1054 """
1055 Find config files which are appropriate values for varname.
1056 i.e. MACHINE, DISTRO
1057 """
1058 possible = []
1059 var = varname.lower()
1060
1061 data = self.data
1062 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001063 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001064 for path in bbpaths:
1065 confpath = os.path.join(path, "conf", var)
1066 if os.path.exists(confpath):
1067 for root, dirs, files in os.walk(confpath):
1068 # get all child files, these are appropriate values
1069 for f in files:
1070 val, sep, end = f.rpartition('.')
1071 if end == 'conf':
1072 possible.append(val)
1073
1074 if possible:
1075 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1076
1077 def findInheritsClass(self, klass):
1078 """
1079 Find all recipes which inherit the specified class
1080 """
1081 pkg_list = []
1082
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001083 for pfn in self.recipecaches[''].pkg_fn:
1084 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001085 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001086 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001087
1088 return pkg_list
1089
1090 def generateTargetsTree(self, klass=None, pkgs=None):
1091 """
1092 Generate a dependency tree of buildable targets
1093 Generate an event with the result
1094 """
1095 # if the caller hasn't specified a pkgs list default to universe
1096 if not pkgs:
1097 pkgs = ['universe']
1098 # if inherited_class passed ensure all recipes which inherit the
1099 # specified class are included in pkgs
1100 if klass:
1101 extra_pkgs = self.findInheritsClass(klass)
1102 pkgs = pkgs + extra_pkgs
1103
1104 # generate a dependency tree for all our packages
1105 tree = self.generatePkgDepTreeData(pkgs, 'build')
1106 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1107
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001108 def interactiveMode( self ):
1109 """Drop off into a shell"""
1110 try:
1111 from bb import shell
1112 except ImportError:
1113 parselog.exception("Interactive mode not available")
1114 sys.exit(1)
1115 else:
1116 shell.start( self )
1117
1118
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001119 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001120 """Handle collections"""
1121 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001122 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001123 if collections:
1124 collection_priorities = {}
1125 collection_depends = {}
1126 collection_list = collections.split()
1127 min_prio = 0
1128 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001129 bb.debug(1,'Processing %s in collection list' % (c))
1130
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001131 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001132 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001133 if priority:
1134 try:
1135 prio = int(priority)
1136 except ValueError:
1137 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1138 errors = True
1139 if min_prio == 0 or prio < min_prio:
1140 min_prio = prio
1141 collection_priorities[c] = prio
1142 else:
1143 collection_priorities[c] = None
1144
1145 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001146 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001147 if deps:
1148 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001149 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001150 except bb.utils.VersionStringException as vse:
1151 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001152 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001153 if dep in collection_list:
1154 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001155 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001156 (op, depver) = opstr.split()
1157 if layerver:
1158 try:
1159 res = bb.utils.vercmp_string_op(layerver, depver, op)
1160 except bb.utils.VersionStringException as vse:
1161 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1162 if not res:
1163 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)
1164 errors = True
1165 else:
1166 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)
1167 errors = True
1168 else:
1169 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1170 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001171 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001172 else:
1173 collection_depends[c] = []
1174
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001175 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001176 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001177 if recs:
1178 try:
1179 recDict = bb.utils.explode_dep_versions2(recs)
1180 except bb.utils.VersionStringException as vse:
1181 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1182 for rec, oplist in list(recDict.items()):
1183 if rec in collection_list:
1184 if oplist:
1185 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001186 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001187 if layerver:
1188 (op, recver) = opstr.split()
1189 try:
1190 res = bb.utils.vercmp_string_op(layerver, recver, op)
1191 except bb.utils.VersionStringException as vse:
1192 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1193 if not res:
1194 parselog.debug(3,"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)
1195 continue
1196 else:
1197 parselog.debug(3,"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)
1198 continue
1199 parselog.debug(3,"Layer '%s' recommends layer '%s', so we are adding it", c, rec)
1200 collection_depends[c].append(rec)
1201 else:
1202 parselog.debug(3,"Layer '%s' recommends layer '%s', but this layer is not enabled in your configuration", c, rec)
1203
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001204 # Recursively work out collection priorities based on dependencies
1205 def calc_layer_priority(collection):
1206 if not collection_priorities[collection]:
1207 max_depprio = min_prio
1208 for dep in collection_depends[collection]:
1209 calc_layer_priority(dep)
1210 depprio = collection_priorities[dep]
1211 if depprio > max_depprio:
1212 max_depprio = depprio
1213 max_depprio += 1
1214 parselog.debug(1, "Calculated priority of layer %s as %d", collection, max_depprio)
1215 collection_priorities[collection] = max_depprio
1216
1217 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1218 for c in collection_list:
1219 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001220 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001221 if regex is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001222 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1223 errors = True
1224 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001225 elif regex == "":
1226 parselog.debug(1, "BBFILE_PATTERN_%s is empty" % c)
Brad Bishop19323692019-04-05 15:28:33 -04001227 cre = re.compile('^NULL$')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001228 errors = False
1229 else:
1230 try:
1231 cre = re.compile(regex)
1232 except re.error:
1233 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1234 errors = True
1235 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001236 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001237 if errors:
1238 # We've already printed the actual error(s)
1239 raise CollectionError("Errors during parsing layer configuration")
1240
1241 def buildSetVars(self):
1242 """
1243 Setup any variables needed before starting a build
1244 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001245 t = time.gmtime()
1246 for mc in self.databuilder.mcdata:
1247 ds = self.databuilder.mcdata[mc]
1248 if not ds.getVar("BUILDNAME", False):
1249 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1250 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1251 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1252 ds.setVar("TIME", time.strftime('%H%M%S', t))
1253
1254 def reset_mtime_caches(self):
1255 """
1256 Reset mtime caches - this is particularly important when memory resident as something
1257 which is cached is not unlikely to have changed since the last invocation (e.g. a
1258 file associated with a recipe might have been modified by the user).
1259 """
1260 build.reset_cache()
1261 bb.fetch._checksum_cache.mtime_cache.clear()
1262 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1263 if siggen_cache:
1264 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001265
Andrew Geissler5a43b432020-06-13 10:46:56 -05001266 def matchFiles(self, bf, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001267 """
1268 Find the .bb files which match the expression in 'buildfile'.
1269 """
1270 if bf.startswith("/") or bf.startswith("../"):
1271 bf = os.path.abspath(bf)
1272
Andrew Geissler5a43b432020-06-13 10:46:56 -05001273 self.collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)}
1274 filelist, masked, searchdirs = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001275 try:
1276 os.stat(bf)
1277 bf = os.path.abspath(bf)
1278 return [bf]
1279 except OSError:
1280 regexp = re.compile(bf)
1281 matches = []
1282 for f in filelist:
1283 if regexp.search(f) and os.path.isfile(f):
1284 matches.append(f)
1285 return matches
1286
Andrew Geissler5a43b432020-06-13 10:46:56 -05001287 def matchFile(self, buildfile, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001288 """
1289 Find the .bb file which matches the expression in 'buildfile'.
1290 Raise an error if multiple files
1291 """
Andrew Geissler5a43b432020-06-13 10:46:56 -05001292 matches = self.matchFiles(buildfile, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001293 if len(matches) != 1:
1294 if matches:
1295 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1296 if matches:
1297 for f in matches:
1298 msg += "\n %s" % f
1299 parselog.error(msg)
1300 else:
1301 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1302 raise NoSpecificMatch
1303 return matches[0]
1304
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001305 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001306 """
1307 Build the file matching regexp buildfile
1308 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001309 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001310
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001311 # Too many people use -b because they think it's how you normally
1312 # specify a target to be built, so show a warning
1313 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1314
1315 self.buildFileInternal(buildfile, task)
1316
1317 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1318 """
1319 Build the file matching regexp buildfile
1320 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001321
1322 # Parse the configuration here. We need to do it explicitly here since
1323 # buildFile() doesn't use the cache
1324 self.parseConfiguration()
1325
1326 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001327 if task is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001328 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001329 if not task.startswith("do_"):
1330 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001331
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001332 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001333 fn = self.matchFile(fn, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334
1335 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001336 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001337
Andrew Geissler5a43b432020-06-13 10:46:56 -05001338 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001339
Andrew Geissler5a43b432020-06-13 10:46:56 -05001340 infos = bb_caches[mc].parse(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001341 infos = dict(infos)
1342
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001343 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001344 try:
1345 info_array = infos[fn]
1346 except KeyError:
1347 bb.fatal("%s does not exist" % fn)
1348
1349 if info_array[0].skipped:
1350 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1351
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001352 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001353
1354 # Tweak some variables
1355 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001356 self.recipecaches[mc].ignored_dependencies = set()
1357 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001358 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001359
1360 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001361 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1362 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001363 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1364 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001365
1366 # Invalidate task for target if force mode active
1367 if self.configuration.force:
1368 logger.verbose("Invalidate task %s, %s", task, fn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001369 bb.parse.siggen.invalidate_task(task, self.recipecaches[mc], fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001370
1371 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001372 taskdata = {}
1373 taskdata[mc] = bb.taskdata.TaskData(self.configuration.abort)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001374 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001375
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001376 if quietlog:
1377 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1378 bb.runqueue.logger.setLevel(logging.WARNING)
1379
1380 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1381 if fireevents:
1382 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001383
1384 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001385 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001386
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001387 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001388
1389 def buildFileIdle(server, rq, abort):
1390
1391 msg = None
1392 interrupted = 0
1393 if abort or self.state == state.forceshutdown:
1394 rq.finish_runqueue(True)
1395 msg = "Forced shutdown"
1396 interrupted = 2
1397 elif self.state == state.shutdown:
1398 rq.finish_runqueue(False)
1399 msg = "Stopped build"
1400 interrupted = 1
1401 failures = 0
1402 try:
1403 retval = rq.execute_runqueue()
1404 except runqueue.TaskFailure as exc:
1405 failures += len(exc.args)
1406 retval = False
1407 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001408 self.command.finishAsyncCommand(str(exc))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001409 if quietlog:
1410 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001411 return False
1412
1413 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001414 if fireevents:
1415 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, item, failures, interrupted), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001416 self.command.finishAsyncCommand(msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001417 # We trashed self.recipecaches above
1418 self.parsecache_valid = False
1419 self.configuration.limited_deps = False
1420 bb.parse.siggen.reset(self.data)
1421 if quietlog:
1422 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001423 return False
1424 if retval is True:
1425 return True
1426 return retval
1427
Andrew Geissler635e0e42020-08-21 15:58:33 -05001428 self.idleCallBackRegister(buildFileIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001429
1430 def buildTargets(self, targets, task):
1431 """
1432 Attempt to build the targets specified
1433 """
1434
1435 def buildTargetsIdle(server, rq, abort):
1436 msg = None
1437 interrupted = 0
1438 if abort or self.state == state.forceshutdown:
1439 rq.finish_runqueue(True)
1440 msg = "Forced shutdown"
1441 interrupted = 2
1442 elif self.state == state.shutdown:
1443 rq.finish_runqueue(False)
1444 msg = "Stopped build"
1445 interrupted = 1
1446 failures = 0
1447 try:
1448 retval = rq.execute_runqueue()
1449 except runqueue.TaskFailure as exc:
1450 failures += len(exc.args)
1451 retval = False
1452 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001453 self.command.finishAsyncCommand(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001454 return False
1455
1456 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001457 try:
1458 for mc in self.multiconfigs:
1459 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1460 finally:
1461 self.command.finishAsyncCommand(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001462 return False
1463 if retval is True:
1464 return True
1465 return retval
1466
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001467 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001468 self.buildSetVars()
1469
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001470 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001471 if task is None:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001472 task = self.configuration.cmd
1473
1474 if not task.startswith("do_"):
1475 task = "do_%s" % task
1476
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001477 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1478
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001479 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001480
1481 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.abort)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001482
1483 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001484
1485 # make targets to always look as <target>:do_<task>
1486 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001487 for target in runlist:
1488 if target[0]:
Brad Bishop15ae2502019-06-18 21:44:24 -04001489 ntargets.append("mc:%s:%s:%s" % (target[0], target[1], target[2]))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001490 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001491
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001492 for mc in self.multiconfigs:
1493 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001494
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001495 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001496 if 'universe' in targets:
1497 rq.rqdata.warn_multi_bb = True
1498
Andrew Geissler635e0e42020-08-21 15:58:33 -05001499 self.idleCallBackRegister(buildTargetsIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001500
1501
1502 def getAllKeysWithFlags(self, flaglist):
1503 dump = {}
1504 for k in self.data.keys():
1505 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001506 expand = True
1507 flags = self.data.getVarFlags(k)
1508 if flags and "func" in flags and "python" in flags:
1509 expand = False
1510 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001511 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1512 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001513 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001514 'history' : self.data.varhistory.variable(k),
1515 }
1516 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001517 if flags and d in flags:
1518 dump[k][d] = flags[d]
1519 else:
1520 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001521 except Exception as e:
1522 print(e)
1523 return dump
1524
1525
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001526 def updateCacheSync(self):
1527 if self.state == state.running:
1528 return
1529
1530 # reload files for which we got notifications
1531 for p in self.inotify_modified_files:
1532 bb.parse.update_cache(p)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001533 if p in bb.parse.BBHandler.cached_statements:
1534 del bb.parse.BBHandler.cached_statements[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001535 self.inotify_modified_files = []
1536
1537 if not self.baseconfig_valid:
1538 logger.debug(1, "Reloading base configuration data")
1539 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001540 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001541
1542 # This is called for all async commands when self.state != running
1543 def updateCache(self):
1544 if self.state == state.running:
1545 return
1546
1547 if self.state in (state.shutdown, state.forceshutdown, state.error):
1548 if hasattr(self.parser, 'shutdown'):
1549 self.parser.shutdown(clean=False, force = True)
1550 raise bb.BBHandledException()
1551
1552 if self.state != state.parsing:
1553 self.updateCacheSync()
1554
1555 if self.state != state.parsing and not self.parsecache_valid:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001556 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001557 self.parseConfiguration ()
1558 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001559 for mc in self.multiconfigs:
1560 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001561
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001562 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001563 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001564 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001565
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001566 for dep in self.configuration.extra_assume_provided:
1567 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001568
Andrew Geissler5a43b432020-06-13 10:46:56 -05001569 self.collections = {}
1570
1571 mcfilelist = {}
1572 total_masked = 0
1573 searchdirs = set()
1574 for mc in self.multiconfigs:
1575 self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc)
1576 (filelist, masked, search) = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
1577
1578 mcfilelist[mc] = filelist
1579 total_masked += masked
1580 searchdirs |= set(search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001581
1582 # Add inotify watches for directories searched for bb/bbappend files
1583 for dirent in searchdirs:
1584 self.add_filewatch([[dirent]], dirs=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001585
Andrew Geissler5a43b432020-06-13 10:46:56 -05001586 self.parser = CookerParser(self, mcfilelist, total_masked)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001587 self.parsecache_valid = True
1588
1589 self.state = state.parsing
1590
1591 if not self.parser.parse_next():
1592 collectlog.debug(1, "parsing complete")
1593 if self.parser.error:
1594 raise bb.BBHandledException()
1595 self.show_appends_with_no_recipes()
1596 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001597 for mc in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001598 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 -05001599 self.state = state.running
1600
1601 # Send an event listing all stamps reachable after parsing
1602 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001603 for mc in self.multiconfigs:
1604 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1605 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001606 return None
1607
1608 return True
1609
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001610 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001611
1612 # Return a copy, don't modify the original
1613 pkgs_to_build = pkgs_to_build[:]
1614
1615 if len(pkgs_to_build) == 0:
1616 raise NothingToBuild
1617
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001618 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001619 for pkg in pkgs_to_build.copy():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001620 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001621 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Brad Bishop15ae2502019-06-18 21:44:24 -04001622 if pkg.startswith("multiconfig:"):
1623 pkgs_to_build.remove(pkg)
1624 pkgs_to_build.append(pkg.replace("multiconfig:", "mc:"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001625
1626 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001627 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001628 for mc in self.multiconfigs:
1629 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1630 for t in self.recipecaches[mc].world_target:
1631 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001632 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001633 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001634
1635 if 'universe' in pkgs_to_build:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001636 parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001637 parselog.debug(1, "collating packages for \"universe\"")
1638 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001639 for mc in self.multiconfigs:
1640 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001641 if task:
1642 foundtask = False
1643 for provider_fn in self.recipecaches[mc].providers[t]:
1644 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1645 foundtask = True
1646 break
1647 if not foundtask:
1648 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1649 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001650 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001651 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001652 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001653
1654 return pkgs_to_build
1655
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001656 def pre_serve(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001657 # We now are in our own process so we can call this here.
1658 # PRServ exits if its parent process exits
1659 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001660 return
1661
1662 def post_serve(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001663 prserv.serv.auto_shutdown()
Brad Bishop08902b02019-08-20 09:16:51 -04001664 if self.hashserv:
1665 self.hashserv.process.terminate()
1666 self.hashserv.process.join()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001667 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001668
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001669 def shutdown(self, force = False):
1670 if force:
1671 self.state = state.forceshutdown
1672 else:
1673 self.state = state.shutdown
1674
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001675 if self.parser:
1676 self.parser.shutdown(clean=not force, force=force)
1677
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001678 def finishcommand(self):
1679 self.state = state.initial
1680
1681 def reset(self):
1682 self.initConfigurationData()
Brad Bishop08902b02019-08-20 09:16:51 -04001683 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001684
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001685 def clientComplete(self):
1686 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001687 self.finishcommand()
1688 self.extraconfigdata = {}
1689 self.command.reset()
1690 self.databuilder.reset()
1691 self.data = self.databuilder.data
Andrew Geissler82c905d2020-04-13 13:39:40 -05001692 self.parsecache_valid = False
1693 self.baseconfig_valid = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001694
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001695
1696class CookerExit(bb.event.Event):
1697 """
1698 Notify clients of the Cooker shutdown
1699 """
1700
1701 def __init__(self):
1702 bb.event.Event.__init__(self)
1703
1704
1705class CookerCollectFiles(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001706 def __init__(self, priorities, mc=''):
1707 self.mc = mc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001708 self.bbappends = []
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001709 # Priorities is a list of tupples, with the second element as the pattern.
1710 # We need to sort the list with the longest pattern first, and so on to
1711 # the shortest. This allows nested layers to be properly evaluated.
1712 self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001713
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001714 def calc_bbfile_priority(self, filename):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001715 for _, _, regex, pri in self.bbfile_config_priorities:
1716 if regex.match(filename):
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001717 return pri, regex
1718 return 0, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001719
1720 def get_bbfiles(self):
1721 """Get list of default .bb files by reading out the current directory"""
1722 path = os.getcwd()
1723 contents = os.listdir(path)
1724 bbfiles = []
1725 for f in contents:
1726 if f.endswith(".bb"):
1727 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1728 return bbfiles
1729
1730 def find_bbfiles(self, path):
1731 """Find all the .bb and .bbappend files in a directory"""
1732 found = []
1733 for dir, dirs, files in os.walk(path):
1734 for ignored in ('SCCS', 'CVS', '.svn'):
1735 if ignored in dirs:
1736 dirs.remove(ignored)
1737 found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
1738
1739 return found
1740
1741 def collect_bbfiles(self, config, eventdata):
1742 """Collect all available .bb build files"""
1743 masked = 0
1744
1745 collectlog.debug(1, "collecting .bb files")
1746
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001747 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001748 config.setVar("BBFILES", " ".join(files))
1749
1750 # Sort files by priority
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001751 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem)[0] )
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001752
1753 if not len(files):
1754 files = self.get_bbfiles()
1755
1756 if not len(files):
1757 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1758 bb.event.fire(CookerExit(), eventdata)
1759
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001760 # We need to track where we look so that we can add inotify watches. There
1761 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001762 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001763 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001764 if hasattr(os, 'scandir'):
1765 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001766 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001767
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001768 def ourlistdir(d):
1769 searchdirs.append(d)
1770 return origlistdir(d)
1771
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001772 def ourscandir(d):
1773 searchdirs.append(d)
1774 return origscandir(d)
1775
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001776 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001777 if hasattr(os, 'scandir'):
1778 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001779 try:
1780 # Can't use set here as order is important
1781 newfiles = []
1782 for f in files:
1783 if os.path.isdir(f):
1784 dirfiles = self.find_bbfiles(f)
1785 for g in dirfiles:
1786 if g not in newfiles:
1787 newfiles.append(g)
1788 else:
1789 globbed = glob.glob(f)
1790 if not globbed and os.path.exists(f):
1791 globbed = [f]
1792 # glob gives files in order on disk. Sort to be deterministic.
1793 for g in sorted(globbed):
1794 if g not in newfiles:
1795 newfiles.append(g)
1796 finally:
1797 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001798 if hasattr(os, 'scandir'):
1799 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001800
1801 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001802
1803 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001804 # First validate the individual regular expressions and ignore any
1805 # that do not compile
1806 bbmasks = []
1807 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001808 # When constructing an older style single regex, it's possible for BBMASK
1809 # to end up beginning with '|', which matches and masks _everything_.
1810 if mask.startswith("|"):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001811 collectlog.warning("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001812 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001813 try:
1814 re.compile(mask)
1815 bbmasks.append(mask)
1816 except sre_constants.error:
1817 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1818
1819 # Then validate the combined regular expressions. This should never
1820 # fail, but better safe than sorry...
1821 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001822 try:
1823 bbmask_compiled = re.compile(bbmask)
1824 except sre_constants.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001825 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1826 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001827
1828 bbfiles = []
1829 bbappend = []
1830 for f in newfiles:
1831 if bbmask and bbmask_compiled.search(f):
1832 collectlog.debug(1, "skipping masked file %s", f)
1833 masked += 1
1834 continue
1835 if f.endswith('.bb'):
1836 bbfiles.append(f)
1837 elif f.endswith('.bbappend'):
1838 bbappend.append(f)
1839 else:
1840 collectlog.debug(1, "skipping %s: unknown file extension", f)
1841
1842 # Build a list of .bbappend files for each .bb file
1843 for f in bbappend:
1844 base = os.path.basename(f).replace('.bbappend', '.bb')
1845 self.bbappends.append((base, f))
1846
1847 # Find overlayed recipes
1848 # bbfiles will be in priority order which makes this easy
1849 bbfile_seen = dict()
1850 self.overlayed = defaultdict(list)
1851 for f in reversed(bbfiles):
1852 base = os.path.basename(f)
1853 if base not in bbfile_seen:
1854 bbfile_seen[base] = f
1855 else:
1856 topfile = bbfile_seen[base]
1857 self.overlayed[topfile].append(f)
1858
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001859 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001860
1861 def get_file_appends(self, fn):
1862 """
1863 Returns a list of .bbappend files to apply to fn
1864 """
1865 filelist = []
1866 f = os.path.basename(fn)
1867 for b in self.bbappends:
1868 (bbappend, filename) = b
1869 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1870 filelist.append(filename)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001871 return tuple(filelist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001872
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001873 def collection_priorities(self, pkgfns, fns, d):
1874 # Return the priorities of the entries in pkgfns
1875 # Also check that all the regexes in self.bbfile_config_priorities are used
1876 # (but to do that we need to ensure skipped recipes aren't counted, nor
1877 # collections in BBFILE_PATTERN_IGNORE_EMPTY)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001878
1879 priorities = {}
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001880 seen = set()
1881 matched = set()
1882
1883 matched_regex = set()
1884 unmatched_regex = set()
1885 for _, _, regex, _ in self.bbfile_config_priorities:
1886 unmatched_regex.add(regex)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001887
1888 # Calculate priorities for each file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001889 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001890 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001891 priorities[p], regex = self.calc_bbfile_priority(realfn)
1892 if regex in unmatched_regex:
1893 matched_regex.add(regex)
1894 unmatched_regex.remove(regex)
1895 seen.add(realfn)
1896 if regex:
1897 matched.add(realfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001898
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001899 if unmatched_regex:
1900 # Account for bbappend files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001901 for b in self.bbappends:
1902 (bbfile, append) = b
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001903 seen.add(append)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001904
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001905 # Account for skipped recipes
1906 seen.update(fns)
1907
1908 seen.difference_update(matched)
1909
1910 def already_matched(fn):
1911 for regex in matched_regex:
1912 if regex.match(fn):
1913 return True
1914 return False
1915
1916 for unmatch in unmatched_regex.copy():
1917 for fn in seen:
1918 if unmatch.match(fn):
1919 # If the bbappend or file was already matched by another regex, skip it
1920 # e.g. for a layer within a layer, the outer regex could match, the inner
1921 # regex may match nothing and we should warn about that
1922 if already_matched(fn):
1923 continue
1924 unmatched_regex.remove(unmatch)
1925 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001926
1927 for collection, pattern, regex, _ in self.bbfile_config_priorities:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001928 if regex in unmatched_regex:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001929 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Andrew Geissler5a43b432020-06-13 10:46:56 -05001930 collectlog.warning("No bb files in %s matched BBFILE_PATTERN_%s '%s'" % (self.mc if self.mc else 'default',
1931 collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001932
1933 return priorities
1934
1935class ParsingFailure(Exception):
1936 def __init__(self, realexception, recipe):
1937 self.realexception = realexception
1938 self.recipe = recipe
1939 Exception.__init__(self, realexception, recipe)
1940
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001941class Parser(multiprocessing.Process):
1942 def __init__(self, jobs, results, quit, init, profile):
1943 self.jobs = jobs
1944 self.results = results
1945 self.quit = quit
1946 self.init = init
1947 multiprocessing.Process.__init__(self)
1948 self.context = bb.utils.get_context().copy()
1949 self.handlers = bb.event.get_class_handlers().copy()
1950 self.profile = profile
1951
1952 def run(self):
1953
1954 if not self.profile:
1955 self.realrun()
1956 return
1957
1958 try:
1959 import cProfile as profile
1960 except:
1961 import profile
1962 prof = profile.Profile()
1963 try:
1964 profile.Profile.runcall(prof, self.realrun)
1965 finally:
1966 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
1967 prof.dump_stats(logfile)
1968
1969 def realrun(self):
1970 if self.init:
1971 self.init()
1972
1973 pending = []
1974 while True:
1975 try:
1976 self.quit.get_nowait()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001977 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001978 pass
1979 else:
1980 self.results.cancel_join_thread()
1981 break
1982
1983 if pending:
1984 result = pending.pop()
1985 else:
1986 try:
Brad Bishop19323692019-04-05 15:28:33 -04001987 job = self.jobs.pop()
1988 except IndexError:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001989 break
1990 result = self.parse(*job)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001991 # Clear the siggen cache after parsing to control memory usage, its huge
1992 bb.parse.siggen.postparsing_clean_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001993 try:
1994 self.results.put(result, timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001995 except queue.Full:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001996 pending.append(result)
1997
Andrew Geissler5a43b432020-06-13 10:46:56 -05001998 def parse(self, mc, cache, filename, appends):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001999 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002000 origfilter = bb.event.LogHandler.filter
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002001 # Record the filename we're parsing into any events generated
2002 def parse_filter(self, record):
2003 record.taskpid = bb.event.worker_pid
2004 record.fn = filename
2005 return True
2006
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002007 # Reset our environment and handlers to the original settings
2008 bb.utils.set_context(self.context.copy())
2009 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002010 bb.event.LogHandler.filter = parse_filter
2011
Andrew Geissler5a43b432020-06-13 10:46:56 -05002012 return True, mc, cache.parse(filename, appends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002013 except Exception as exc:
2014 tb = sys.exc_info()[2]
2015 exc.recipe = filename
2016 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
2017 return True, exc
2018 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
2019 # and for example a worker thread doesn't just exit on its own in response to
2020 # a SystemExit event for example.
2021 except BaseException as exc:
2022 return True, ParsingFailure(exc, filename)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002023 finally:
2024 bb.event.LogHandler.filter = origfilter
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002025
2026class CookerParser(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002027 def __init__(self, cooker, mcfilelist, masked):
2028 self.mcfilelist = mcfilelist
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002029 self.cooker = cooker
2030 self.cfgdata = cooker.data
2031 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002032 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002033
2034 # Accounting statistics
2035 self.parsed = 0
2036 self.cached = 0
2037 self.error = 0
2038 self.masked = masked
2039
2040 self.skipped = 0
2041 self.virtuals = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002042
2043 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002044 self.process_names = []
2045
Andrew Geissler5a43b432020-06-13 10:46:56 -05002046 self.bb_caches = bb.cache.MulticonfigCache(self.cfgbuilder, self.cfghash, cooker.caches_array)
2047 self.fromcache = set()
2048 self.willparse = set()
2049 for mc in self.cooker.multiconfigs:
2050 for filename in self.mcfilelist[mc]:
2051 appends = self.cooker.collections[mc].get_file_appends(filename)
2052 if not self.bb_caches[mc].cacheValid(filename, appends):
2053 self.willparse.add((mc, self.bb_caches[mc], filename, appends))
2054 else:
2055 self.fromcache.add((mc, self.bb_caches[mc], filename, appends))
2056
2057 self.total = len(self.fromcache) + len(self.willparse)
2058 self.toparse = len(self.willparse)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002059 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002060
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002061 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Andrew Geissler5a43b432020-06-13 10:46:56 -05002062 multiprocessing.cpu_count()), self.toparse)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002063
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002064 self.start()
2065 self.haveshutdown = False
2066
2067 def start(self):
2068 self.results = self.load_cached()
2069 self.processes = []
2070 if self.toparse:
2071 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
2072 def init():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002073 bb.utils.set_process_name(multiprocessing.current_process().name)
2074 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2075 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002076
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002077 self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002078 self.result_queue = multiprocessing.Queue()
Brad Bishop19323692019-04-05 15:28:33 -04002079
2080 def chunkify(lst,n):
2081 return [lst[i::n] for i in range(n)]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002082 self.jobs = chunkify(list(self.willparse), self.num_processes)
Brad Bishop19323692019-04-05 15:28:33 -04002083
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002084 for i in range(0, self.num_processes):
Brad Bishop19323692019-04-05 15:28:33 -04002085 parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, init, self.cooker.configuration.profile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002086 parser.start()
2087 self.process_names.append(parser.name)
2088 self.processes.append(parser)
2089
2090 self.results = itertools.chain(self.results, self.parse_generator())
2091
2092 def shutdown(self, clean=True, force=False):
2093 if not self.toparse:
2094 return
2095 if self.haveshutdown:
2096 return
2097 self.haveshutdown = True
2098
2099 if clean:
2100 event = bb.event.ParseCompleted(self.cached, self.parsed,
2101 self.skipped, self.masked,
2102 self.virtuals, self.error,
2103 self.total)
2104
2105 bb.event.fire(event, self.cfgdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002106 for process in self.processes:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002107 self.parser_quit.put(None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002108 else:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002109 self.parser_quit.cancel_join_thread()
2110 for process in self.processes:
2111 self.parser_quit.put(None)
2112
Brad Bishop08902b02019-08-20 09:16:51 -04002113 # Cleanup the queue before call process.join(), otherwise there might be
2114 # deadlocks.
2115 while True:
2116 try:
2117 self.result_queue.get(timeout=0.25)
2118 except queue.Empty:
2119 break
2120
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002121 for process in self.processes:
2122 if force:
2123 process.join(.1)
2124 process.terminate()
2125 else:
2126 process.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002127
Andrew Geissler5a43b432020-06-13 10:46:56 -05002128 def sync_caches():
2129 for c in self.bb_caches.values():
2130 c.sync()
2131
2132 sync = threading.Thread(target=sync_caches)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002133 sync.start()
2134 multiprocessing.util.Finalize(None, sync.join, exitpriority=-100)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002135 bb.codeparser.parser_cache_savemerge()
2136 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002137 if self.cooker.configuration.profile:
2138 profiles = []
2139 for i in self.process_names:
2140 logfile = "profile-parse-%s.log" % i
2141 if os.path.exists(logfile):
2142 profiles.append(logfile)
2143
2144 pout = "profile-parse.log.processed"
2145 bb.utils.process_profilelog(profiles, pout = pout)
2146 print("Processed parsing statistics saved to %s" % (pout))
2147
2148 def load_cached(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002149 for mc, cache, filename, appends in self.fromcache:
2150 cached, infos = cache.load(filename, appends)
2151 yield not cached, mc, infos
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002152
2153 def parse_generator(self):
2154 while True:
2155 if self.parsed >= self.toparse:
2156 break
2157
2158 try:
2159 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002160 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002161 pass
2162 else:
2163 value = result[1]
2164 if isinstance(value, BaseException):
2165 raise value
2166 else:
2167 yield result
2168
2169 def parse_next(self):
2170 result = []
2171 parsed = None
2172 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002173 parsed, mc, result = next(self.results)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002174 except StopIteration:
2175 self.shutdown()
2176 return False
2177 except bb.BBHandledException as exc:
2178 self.error += 1
2179 logger.error('Failed to parse recipe: %s' % exc.recipe)
2180 self.shutdown(clean=False)
2181 return False
2182 except ParsingFailure as exc:
2183 self.error += 1
2184 logger.error('Unable to parse %s: %s' %
2185 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
2186 self.shutdown(clean=False)
2187 return False
2188 except bb.parse.ParseError as exc:
2189 self.error += 1
2190 logger.error(str(exc))
2191 self.shutdown(clean=False)
2192 return False
2193 except bb.data_smart.ExpansionError as exc:
2194 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002195 bbdir = os.path.dirname(__file__) + os.sep
2196 etype, value, _ = sys.exc_info()
2197 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2198 logger.error('ExpansionError during parsing %s', value.recipe,
2199 exc_info=(etype, value, tb))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002200 self.shutdown(clean=False)
2201 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002202 except Exception as exc:
2203 self.error += 1
2204 etype, value, tb = sys.exc_info()
2205 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002206 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002207 exc_info=(etype, value, exc.traceback))
2208 else:
2209 # Most likely, an exception occurred during raising an exception
2210 import traceback
2211 logger.error('Exception during parse: %s' % traceback.format_exc())
2212 self.shutdown(clean=False)
2213 return False
2214
2215 self.current += 1
2216 self.virtuals += len(result)
2217 if parsed:
2218 self.parsed += 1
2219 if self.parsed % self.progress_chunk == 0:
2220 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2221 self.cfgdata)
2222 else:
2223 self.cached += 1
2224
2225 for virtualfn, info_array in result:
2226 if info_array[0].skipped:
2227 self.skipped += 1
2228 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Andrew Geissler5a43b432020-06-13 10:46:56 -05002229 self.bb_caches[mc].add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002230 parsed=parsed, watcher = self.cooker.add_filewatch)
2231 return True
2232
2233 def reparse(self, filename):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002234 to_reparse = set()
2235 for mc in self.cooker.multiconfigs:
2236 to_reparse.add((mc, filename, self.cooker.collections[mc].get_file_appends(filename)))
2237
2238 for mc, filename, appends in to_reparse:
2239 infos = self.bb_caches[mc].parse(filename, appends)
2240 for vfn, info_array in infos:
2241 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)