blob: 39e10e6133c4c1af4e95b863bae497ce32d44e02 [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
Andrew Geisslerd1e89492021-02-12 15:35:20 -060076 self.rprovides = info.packages + info.rprovides
77 for package in info.packages:
78 self.rprovides += info.rprovides_pkg[package]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050079 elif reason:
80 self.skipreason = reason
81
82
83class CookerFeatures(object):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060084 _feature_list = [HOB_EXTRA_CACHES, BASEDATASTORE_TRACKING, SEND_SANITYEVENTS] = list(range(3))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050085
86 def __init__(self):
87 self._features=set()
88
89 def setFeature(self, f):
90 # validate we got a request for a feature we support
91 if f not in CookerFeatures._feature_list:
92 return
93 self._features.add(f)
94
95 def __contains__(self, f):
96 return f in self._features
97
98 def __iter__(self):
99 return self._features.__iter__()
100
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600101 def __next__(self):
102 return next(self._features)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500103
104
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600105class EventWriter:
106 def __init__(self, cooker, eventfile):
107 self.file_inited = None
108 self.cooker = cooker
109 self.eventfile = eventfile
110 self.event_queue = []
111
112 def write_event(self, event):
113 with open(self.eventfile, "a") as f:
114 try:
115 str_event = codecs.encode(pickle.dumps(event), 'base64').decode('utf-8')
116 f.write("%s\n" % json.dumps({"class": event.__module__ + "." + event.__class__.__name__,
117 "vars": str_event}))
118 except Exception as err:
119 import traceback
120 print(err, traceback.format_exc())
121
122 def send(self, event):
123 if self.file_inited:
124 # we have the file, just write the event
125 self.write_event(event)
126 else:
127 # init on bb.event.BuildStarted
128 name = "%s.%s" % (event.__module__, event.__class__.__name__)
129 if name in ("bb.event.BuildStarted", "bb.cooker.CookerExit"):
130 with open(self.eventfile, "w") as f:
131 f.write("%s\n" % json.dumps({ "allvariables" : self.cooker.getAllKeysWithFlags(["doc", "func"])}))
132
133 self.file_inited = True
134
135 # write pending events
136 for evt in self.event_queue:
137 self.write_event(evt)
138
139 # also write the current event
140 self.write_event(event)
141 else:
142 # queue all events until the file is inited
143 self.event_queue.append(event)
144
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500145#============================================================================#
146# BBCooker
147#============================================================================#
148class BBCooker:
149 """
150 Manages one bitbake build run
151 """
152
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500153 def __init__(self, featureSet=None, idleCallBackRegister=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600154 self.recipecaches = None
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500155 self.eventlog = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500156 self.skiplist = {}
157 self.featureset = CookerFeatures()
158 if featureSet:
159 for f in featureSet:
160 self.featureset.setFeature(f)
161
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500162 self.configuration = bb.cookerdata.CookerConfiguration()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500163
Andrew Geissler635e0e42020-08-21 15:58:33 -0500164 self.idleCallBackRegister = idleCallBackRegister
165
Brad Bishopf058f492019-01-28 23:50:33 -0500166 bb.debug(1, "BBCooker starting %s" % time.time())
167 sys.stdout.flush()
168
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500169 self.configwatcher = pyinotify.WatchManager()
Brad Bishopf058f492019-01-28 23:50:33 -0500170 bb.debug(1, "BBCooker pyinotify1 %s" % time.time())
171 sys.stdout.flush()
172
Andrew Geissler82c905d2020-04-13 13:39:40 -0500173 self.configwatcher.bbseen = set()
174 self.configwatcher.bbwatchedfiles = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500175 self.confignotifier = pyinotify.Notifier(self.configwatcher, self.config_notifications)
Brad Bishopf058f492019-01-28 23:50:33 -0500176 bb.debug(1, "BBCooker pyinotify2 %s" % time.time())
177 sys.stdout.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500178 self.watchmask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CREATE | pyinotify.IN_DELETE | \
179 pyinotify.IN_DELETE_SELF | pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF | \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500180 pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500181 self.watcher = pyinotify.WatchManager()
Brad Bishopf058f492019-01-28 23:50:33 -0500182 bb.debug(1, "BBCooker pyinotify3 %s" % time.time())
183 sys.stdout.flush()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500184 self.watcher.bbseen = set()
185 self.watcher.bbwatchedfiles = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500186 self.notifier = pyinotify.Notifier(self.watcher, self.notifications)
187
Brad Bishopf058f492019-01-28 23:50:33 -0500188 bb.debug(1, "BBCooker pyinotify complete %s" % time.time())
189 sys.stdout.flush()
190
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500191 # If being called by something like tinfoil, we need to clean cached data
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500192 # which may now be invalid
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500193 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500194 bb.parse.BBHandler.cached_statements = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500196 self.ui_cmdline = None
Brad Bishop08902b02019-08-20 09:16:51 -0400197 self.hashserv = None
Brad Bishopa34c0302019-09-23 22:34:48 -0400198 self.hashservaddr = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500199
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500200 self.inotify_modified_files = []
201
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500202 def _process_inotify_updates(server, cooker, abort):
203 cooker.process_inotify_updates()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500204 return 1.0
205
Andrew Geissler635e0e42020-08-21 15:58:33 -0500206 self.idleCallBackRegister(_process_inotify_updates, self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500207
208 # TOSTOP must not be set or our children will hang when they output
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600209 try:
210 fd = sys.stdout.fileno()
211 if os.isatty(fd):
212 import termios
213 tcattr = termios.tcgetattr(fd)
214 if tcattr[3] & termios.TOSTOP:
215 buildlog.info("The terminal had the TOSTOP bit set, clearing...")
216 tcattr[3] = tcattr[3] & ~termios.TOSTOP
217 termios.tcsetattr(fd, termios.TCSANOW, tcattr)
218 except UnsupportedOperation:
219 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500220
221 self.command = bb.command.Command(self)
222 self.state = state.initial
223
224 self.parser = None
225
226 signal.signal(signal.SIGTERM, self.sigterm_exception)
227 # Let SIGHUP exit as SIGTERM
228 signal.signal(signal.SIGHUP, self.sigterm_exception)
229
Brad Bishopf058f492019-01-28 23:50:33 -0500230 bb.debug(1, "BBCooker startup complete %s" % time.time())
231 sys.stdout.flush()
232
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500233 def init_configdata(self):
234 if not hasattr(self, "data"):
235 self.initConfigurationData()
236 bb.debug(1, "BBCooker parsed base configuration %s" % time.time())
237 sys.stdout.flush()
238 self.handlePRServ()
239
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500240 def process_inotify_updates(self):
241 for n in [self.confignotifier, self.notifier]:
242 if n.check_events(timeout=0):
243 # read notified events and enqeue them
244 n.read_events()
245 n.process_events()
246
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500247 def config_notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500248 if event.maskname == "IN_Q_OVERFLOW":
249 bb.warn("inotify event queue overflowed, invalidating caches.")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500250 self.parsecache_valid = False
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500251 self.baseconfig_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500252 bb.parse.clear_cache()
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500253 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500254 if not event.pathname in self.configwatcher.bbwatchedfiles:
255 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500256 if not event.pathname in self.inotify_modified_files:
257 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500258 self.baseconfig_valid = False
259
260 def notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500261 if event.maskname == "IN_Q_OVERFLOW":
262 bb.warn("inotify event queue overflowed, invalidating caches.")
263 self.parsecache_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500264 bb.parse.clear_cache()
265 return
266 if event.pathname.endswith("bitbake-cookerdaemon.log") \
267 or event.pathname.endswith("bitbake.lock"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500268 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500269 if not event.pathname in self.inotify_modified_files:
270 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271 self.parsecache_valid = False
272
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500273 def add_filewatch(self, deps, watcher=None, dirs=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274 if not watcher:
275 watcher = self.watcher
276 for i in deps:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500277 watcher.bbwatchedfiles.add(i[0])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500278 if dirs:
279 f = i[0]
280 else:
281 f = os.path.dirname(i[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500282 if f in watcher.bbseen:
283 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -0500284 watcher.bbseen.add(f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500285 watchtarget = None
286 while True:
287 # We try and add watches for files that don't exist but if they did, would influence
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500288 # the parser. The parent directory of these files may not exist, in which case we need
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500289 # to watch any parent that does exist for changes.
290 try:
291 watcher.add_watch(f, self.watchmask, quiet=False)
292 if watchtarget:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500293 watcher.bbwatchedfiles.add(watchtarget)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500294 break
295 except pyinotify.WatchManagerError as e:
296 if 'ENOENT' in str(e):
297 watchtarget = f
298 f = os.path.dirname(f)
299 if f in watcher.bbseen:
300 break
Andrew Geissler82c905d2020-04-13 13:39:40 -0500301 watcher.bbseen.add(f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500302 continue
303 if 'ENOSPC' in str(e):
304 providerlog.error("No space left on device or exceeds fs.inotify.max_user_watches?")
305 providerlog.error("To check max_user_watches: sysctl -n fs.inotify.max_user_watches.")
306 providerlog.error("To modify max_user_watches: sysctl -n -w fs.inotify.max_user_watches=<value>.")
307 providerlog.error("Root privilege is required to modify max_user_watches.")
308 raise
309
310 def sigterm_exception(self, signum, stackframe):
311 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500312 bb.warn("Cooker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500313 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500314 bb.warn("Cooker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500315 self.state = state.forceshutdown
316
317 def setFeatures(self, features):
318 # we only accept a new feature set if we're in state initial, so we can reset without problems
319 if not self.state in [state.initial, state.shutdown, state.forceshutdown, state.stopped, state.error]:
320 raise Exception("Illegal state for feature set change")
321 original_featureset = list(self.featureset)
322 for feature in features:
323 self.featureset.setFeature(feature)
324 bb.debug(1, "Features set %s (was %s)" % (original_featureset, list(self.featureset)))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500325 if (original_featureset != list(self.featureset)) and self.state != state.error and hasattr(self, "data"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500326 self.reset()
327
328 def initConfigurationData(self):
329
330 self.state = state.initial
331 self.caches_array = []
332
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500333 # Need to preserve BB_CONSOLELOG over resets
334 consolelog = None
335 if hasattr(self, "data"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500336 consolelog = self.data.getVar("BB_CONSOLELOG")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500337
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500338 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
339 self.enableDataTracking()
340
341 all_extra_cache_names = []
342 # We hardcode all known cache types in a single place, here.
343 if CookerFeatures.HOB_EXTRA_CACHES in self.featureset:
344 all_extra_cache_names.append("bb.cache_extra:HobRecipeInfo")
345
346 caches_name_array = ['bb.cache:CoreRecipeInfo'] + all_extra_cache_names
347
348 # At least CoreRecipeInfo will be loaded, so caches_array will never be empty!
349 # This is the entry point, no further check needed!
350 for var in caches_name_array:
351 try:
352 module_name, cache_name = var.split(':')
353 module = __import__(module_name, fromlist=(cache_name,))
354 self.caches_array.append(getattr(module, cache_name))
355 except ImportError as exc:
356 logger.critical("Unable to import extra RecipeInfo '%s' from '%s': %s" % (cache_name, module_name, exc))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500357 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500358
359 self.databuilder = bb.cookerdata.CookerDataBuilder(self.configuration, False)
360 self.databuilder.parseBaseConfiguration()
361 self.data = self.databuilder.data
362 self.data_hash = self.databuilder.data_hash
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500363 self.extraconfigdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500364
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500365 if consolelog:
366 self.data.setVar("BB_CONSOLELOG", consolelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500367
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500368 self.data.setVar('BB_CMDLINE', self.ui_cmdline)
369
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500370 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
371 self.disableDataTracking()
372
Brad Bishop15ae2502019-06-18 21:44:24 -0400373 for mc in self.databuilder.mcdata.values():
374 mc.renameVar("__depends", "__base_depends")
375 self.add_filewatch(mc.getVar("__base_depends", False), self.configwatcher)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500376
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500377 self.baseconfig_valid = True
378 self.parsecache_valid = False
379
380 def handlePRServ(self):
381 # Setup a PR Server based on the new configuration
382 try:
383 self.prhost = prserv.serv.auto_start(self.data)
384 except prserv.serv.PRServiceConfigError as e:
385 bb.fatal("Unable to start PR Server, exitting")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500386
Brad Bishopa34c0302019-09-23 22:34:48 -0400387 if self.data.getVar("BB_HASHSERVE") == "auto":
388 # Create a new hash server bound to a unix domain socket
Brad Bishop08902b02019-08-20 09:16:51 -0400389 if not self.hashserv:
390 dbfile = (self.data.getVar("PERSISTENT_DIR") or self.data.getVar("CACHE")) + "/hashserv.db"
Brad Bishopa34c0302019-09-23 22:34:48 -0400391 self.hashservaddr = "unix://%s/hashserve.sock" % self.data.getVar("TOPDIR")
392 self.hashserv = hashserv.create_server(self.hashservaddr, dbfile, sync=False)
Brad Bishop08902b02019-08-20 09:16:51 -0400393 self.hashserv.process = multiprocessing.Process(target=self.hashserv.serve_forever)
Brad Bishop08902b02019-08-20 09:16:51 -0400394 self.hashserv.process.start()
Brad Bishopa34c0302019-09-23 22:34:48 -0400395 self.data.setVar("BB_HASHSERVE", self.hashservaddr)
396 self.databuilder.origdata.setVar("BB_HASHSERVE", self.hashservaddr)
397 self.databuilder.data.setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400398 for mc in self.databuilder.mcdata:
Brad Bishopa34c0302019-09-23 22:34:48 -0400399 self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400400
401 bb.parse.init_parser(self.data)
402
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500403 def enableDataTracking(self):
404 self.configuration.tracking = True
405 if hasattr(self, "data"):
406 self.data.enableTracking()
407
408 def disableDataTracking(self):
409 self.configuration.tracking = False
410 if hasattr(self, "data"):
411 self.data.disableTracking()
412
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500413 def parseConfiguration(self):
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600414 self.updateCacheSync()
415
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500416 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500417 nice = self.data.getVar("BB_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500418 if nice:
419 curnice = os.nice(0)
420 nice = int(nice) - curnice
421 buildlog.verbose("Renice to %s " % os.nice(nice))
422
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600423 if self.recipecaches:
424 del self.recipecaches
425 self.multiconfigs = self.databuilder.mcdata.keys()
426 self.recipecaches = {}
427 for mc in self.multiconfigs:
428 self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500429
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500430 self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500431
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500432 self.parsecache_valid = False
433
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500434 def updateConfigOpts(self, options, environment, cmdline):
435 self.ui_cmdline = cmdline
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500436 clean = True
437 for o in options:
438 if o in ['prefile', 'postfile']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500439 # Only these options may require a reparse
440 try:
441 if getattr(self.configuration, o) == options[o]:
442 # Value is the same, no need to mark dirty
443 continue
444 except AttributeError:
445 pass
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600446 logger.debug("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500447 print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500448 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500449 if hasattr(self.configuration, o):
450 setattr(self.configuration, o, options[o])
451
452 if self.configuration.writeeventlog:
453 if self.eventlog and self.eventlog[0] != self.configuration.writeeventlog:
454 bb.event.unregister_UIHhandler(self.eventlog[1])
455 if not self.eventlog or self.eventlog[0] != self.configuration.writeeventlog:
456 # we log all events to a file if so directed
457 # register the log file writer as UI Handler
458 writer = EventWriter(self, self.configuration.writeeventlog)
459 EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
460 self.eventlog = (self.configuration.writeeventlog, bb.event.register_UIHhandler(EventLogWriteHandler(writer)))
461
462 bb.msg.loggerDefaultLogLevel = self.configuration.default_loglevel
463 bb.msg.loggerDefaultDomains = self.configuration.debug_domains
464
465 if hasattr(self, "data"):
466 origenv = bb.data.init()
467 for k in environment:
468 origenv.setVar(k, environment[k])
469 self.data.setVar("BB_ORIGENV", origenv)
470
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471 for k in bb.utils.approved_variables():
472 if k in environment and k not in self.configuration.env:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600473 logger.debug("Updating new environment variable %s to %s" % (k, environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500474 self.configuration.env[k] = environment[k]
475 clean = False
476 if k in self.configuration.env and k not in environment:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600477 logger.debug("Updating environment variable %s (deleted)" % (k))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500478 del self.configuration.env[k]
479 clean = False
480 if k not in self.configuration.env and k not in environment:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500481 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500482 if environment[k] != self.configuration.env[k]:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600483 logger.debug("Updating environment variable %s from %s to %s" % (k, self.configuration.env[k], environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500484 self.configuration.env[k] = environment[k]
485 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500486
487 # Now update all the variables not in the datastore to match
488 self.configuration.env = environment
489
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500490 if not clean:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600491 logger.debug("Base environment change, triggering reparse")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500492 self.reset()
493
494 def runCommands(self, server, data, abort):
495 """
496 Run any queued asynchronous command
497 This is done by the idle handler so it runs in true context rather than
498 tied to any UI.
499 """
500
501 return self.command.runAsyncCommand()
502
503 def showVersions(self):
504
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500505 (latest_versions, preferred_versions, required) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500506
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500507 logger.plain("%-35s %25s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version", "Required Version")
508 logger.plain("%-35s %25s %25s %25s\n", "===========", "==============", "=================", "================")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500509
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500510 for p in sorted(self.recipecaches[''].pkg_pn):
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500511 preferred = preferred_versions[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500512 latest = latest_versions[p]
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500513 requiredstr = ""
514 preferredstr = ""
515 if required[p]:
516 if preferred[0] is not None:
517 requiredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
518 else:
519 bb.fatal("REQUIRED_VERSION of package %s not available" % p)
520 else:
521 preferredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500522
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500523 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
524
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500525 if preferred == latest:
526 preferredstr = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500527
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500528 logger.plain("%-35s %25s %25s %25s", p, lateststr, preferredstr, requiredstr)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500529
530 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
531 """
532 Show the outer or per-recipe environment
533 """
534 fn = None
535 envdata = None
Brad Bishop15ae2502019-06-18 21:44:24 -0400536 mc = ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500537 if not pkgs_to_build:
538 pkgs_to_build = []
539
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500540 orig_tracking = self.configuration.tracking
541 if not orig_tracking:
542 self.enableDataTracking()
543 self.reset()
544
Brad Bishop15ae2502019-06-18 21:44:24 -0400545 def mc_base(p):
546 if p.startswith('mc:'):
547 s = p.split(':')
548 if len(s) == 2:
549 return s[1]
550 return None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500551
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500552 if buildfile:
553 # Parse the configuration here. We need to do it explicitly here since
554 # this showEnvironment() code path doesn't use the cache
555 self.parseConfiguration()
556
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600557 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500558 fn = self.matchFile(fn, mc)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600559 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500560 elif len(pkgs_to_build) == 1:
Brad Bishop15ae2502019-06-18 21:44:24 -0400561 mc = mc_base(pkgs_to_build[0])
562 if not mc:
563 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
564 if pkgs_to_build[0] in set(ignore.split()):
565 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566
Brad Bishop15ae2502019-06-18 21:44:24 -0400567 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.abort, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500568
Brad Bishop15ae2502019-06-18 21:44:24 -0400569 mc = runlist[0][0]
570 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500571
572 if fn:
573 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -0500574 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
575 envdata = bb_caches[mc].loadDataFull(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500576 except Exception as e:
577 parselog.exception("Unable to read %s", fn)
578 raise
Brad Bishop15ae2502019-06-18 21:44:24 -0400579 else:
580 if not mc in self.databuilder.mcdata:
581 bb.fatal('Not multiconfig named "%s" found' % mc)
582 envdata = self.databuilder.mcdata[mc]
583 data.expandKeys(envdata)
584 parse.ast.runAnonFuncs(envdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500585
586 # Display history
587 with closing(StringIO()) as env:
588 self.data.inchistory.emit(env)
589 logger.plain(env.getvalue())
590
591 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500592 with closing(StringIO()) as env:
593 data.emit_env(env, envdata, True)
594 logger.plain(env.getvalue())
595
596 # emit the metadata which isnt valid shell
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500597 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600598 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500599 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500600
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500601 if not orig_tracking:
602 self.disableDataTracking()
603 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500604
605 def buildTaskData(self, pkgs_to_build, task, abort, allowincomplete=False):
606 """
607 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
608 """
609 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
610
611 # A task of None means use the default task
612 if task is None:
613 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500614 if not task.startswith("do_"):
615 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500617 targetlist = self.checkPackages(pkgs_to_build, task)
618 fulltargetlist = []
619 defaulttask_implicit = ''
620 defaulttask_explicit = False
621 wildcard = False
622
623 # Wild card expansion:
Brad Bishop15ae2502019-06-18 21:44:24 -0400624 # Replace string such as "mc:*:bash"
625 # into "mc:A:bash mc:B:bash bash"
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500626 for k in targetlist:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600627 if k.startswith("mc:") and k.count(':') >= 2:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500628 if wildcard:
629 bb.fatal('multiconfig conflict')
630 if k.split(":")[1] == "*":
631 wildcard = True
632 for mc in self.multiconfigs:
633 if mc:
634 fulltargetlist.append(k.replace('*', mc))
635 # implicit default task
636 else:
637 defaulttask_implicit = k.split(":")[2]
638 else:
639 fulltargetlist.append(k)
640 else:
641 defaulttask_explicit = True
642 fulltargetlist.append(k)
643
644 if not defaulttask_explicit and defaulttask_implicit != '':
645 fulltargetlist.append(defaulttask_implicit)
646
647 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600648 taskdata = {}
649 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500650
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600651 for mc in self.multiconfigs:
652 taskdata[mc] = bb.taskdata.TaskData(abort, skiplist=self.skiplist, allowincomplete=allowincomplete)
653 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600654 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500655
656 current = 0
657 runlist = []
658 for k in fulltargetlist:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500659 origk = k
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600660 mc = ""
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600661 if k.startswith("mc:") and k.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600662 mc = k.split(":")[1]
663 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500664 ktask = task
665 if ":do_" in k:
666 k2 = k.split(":do_")
667 k = k2[0]
668 ktask = k2[1]
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500669
670 if mc not in self.multiconfigs:
671 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named %s" % (origk, mc))
672
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600673 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500674 current += 1
675 if not ktask.startswith("do_"):
676 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600677 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
678 # e.g. in ASSUME_PROVIDED
679 continue
680 fn = taskdata[mc].build_targets[k][0]
681 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500682 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600683
Brad Bishop15ae2502019-06-18 21:44:24 -0400684 havemc = False
685 for mc in self.multiconfigs:
686 if taskdata[mc].get_mcdepends():
687 havemc = True
Brad Bishopf058f492019-01-28 23:50:33 -0500688
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800689 # No need to do check providers if there are no mcdeps or not an mc build
Brad Bishop15ae2502019-06-18 21:44:24 -0400690 if havemc or len(self.multiconfigs) > 1:
Andrew Geissler99467da2019-02-25 18:54:23 -0600691 seen = set()
692 new = True
693 # Make sure we can provide the multiconfig dependency
694 while new:
695 mcdeps = set()
696 # Add unresolved first, so we can get multiconfig indirect dependencies on time
697 for mc in self.multiconfigs:
698 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
699 mcdeps |= set(taskdata[mc].get_mcdepends())
700 new = False
701 for mc in self.multiconfigs:
702 for k in mcdeps:
703 if k in seen:
704 continue
705 l = k.split(':')
706 depmc = l[2]
707 if depmc not in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500708 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named configuration %s" % (k,depmc))
Andrew Geissler99467da2019-02-25 18:54:23 -0600709 else:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600710 logger.debug("Adding providers for multiconfig dependency %s" % l[3])
Andrew Geissler99467da2019-02-25 18:54:23 -0600711 taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
712 seen.add(k)
713 new = True
Brad Bishopf058f492019-01-28 23:50:33 -0500714
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600715 for mc in self.multiconfigs:
716 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
717
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500718 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600719 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500720
721 def prepareTreeData(self, pkgs_to_build, task):
722 """
723 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
724 """
725
726 # We set abort to False here to prevent unbuildable targets raising
727 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600728 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500729
730 return runlist, taskdata
731
732 ######## WARNING : this function requires cache_extra to be enabled ########
733
734 def generateTaskDepTreeData(self, pkgs_to_build, task):
735 """
736 Create a dependency graph of pkgs_to_build including reverse dependency
737 information.
738 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500739 if not task.startswith("do_"):
740 task = "do_%s" % task
741
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500742 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600743 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500744 rq.rqdata.prepare()
745 return self.buildDependTree(rq, taskdata)
746
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600747 @staticmethod
748 def add_mc_prefix(mc, pn):
749 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -0400750 return "mc:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600751 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500752
753 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600754 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500755 depend_tree = {}
756 depend_tree["depends"] = {}
757 depend_tree["tdepends"] = {}
758 depend_tree["pn"] = {}
759 depend_tree["rdepends-pn"] = {}
760 depend_tree["packages"] = {}
761 depend_tree["rdepends-pkg"] = {}
762 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500763 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600764 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500765
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600766 for mc in taskdata:
767 for name, fn in list(taskdata[mc].get_providermap().items()):
768 pn = self.recipecaches[mc].pkg_fn[fn]
769 pn = self.add_mc_prefix(mc, pn)
770 if name != pn:
771 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
772 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500773
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600774 for tid in rq.rqdata.runtaskentries:
775 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
776 pn = self.recipecaches[mc].pkg_fn[taskfn]
777 pn = self.add_mc_prefix(mc, pn)
778 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779 if pn not in depend_tree["pn"]:
780 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600781 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500782 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600783 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500784
785 # if we have extra caches, list all attributes they bring in
786 extra_info = []
787 for cache_class in self.caches_array:
788 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
789 cachefields = getattr(cache_class, 'cachefields', [])
790 extra_info = extra_info + cachefields
791
792 # for all attributes stored, add them to the dependency tree
793 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600794 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500795
796
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500797 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
798 if not dotname in depend_tree["tdepends"]:
799 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600800 for dep in rq.rqdata.runtaskentries[tid].depends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800801 (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
802 deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600803 depend_tree["tdepends"][dotname].append("%s.%s" % (deppn, bb.runqueue.taskname_from_tid(dep)))
804 if taskfn not in seen_fns:
805 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500806 packages = []
807
808 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600809 for dep in taskdata[mc].depids[taskfn]:
810 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500811
812 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600813 for rdep in taskdata[mc].rdepids[taskfn]:
814 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500815
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600816 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500817 for package in rdepends:
818 depend_tree["rdepends-pkg"][package] = []
819 for rdepend in rdepends[package]:
820 depend_tree["rdepends-pkg"][package].append(rdepend)
821 packages.append(package)
822
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600823 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500824 for package in rrecs:
825 depend_tree["rrecs-pkg"][package] = []
826 for rdepend in rrecs[package]:
827 depend_tree["rrecs-pkg"][package].append(rdepend)
828 if not package in packages:
829 packages.append(package)
830
831 for package in packages:
832 if package not in depend_tree["packages"]:
833 depend_tree["packages"][package] = {}
834 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600835 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500836 depend_tree["packages"][package]["version"] = version
837
838 return depend_tree
839
840 ######## WARNING : this function requires cache_extra to be enabled ########
841 def generatePkgDepTreeData(self, pkgs_to_build, task):
842 """
843 Create a dependency tree of pkgs_to_build, returning the data.
844 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500845 if not task.startswith("do_"):
846 task = "do_%s" % task
847
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500848 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500849
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600850 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851 depend_tree = {}
852 depend_tree["depends"] = {}
853 depend_tree["pn"] = {}
854 depend_tree["rdepends-pn"] = {}
855 depend_tree["rdepends-pkg"] = {}
856 depend_tree["rrecs-pkg"] = {}
857
858 # if we have extra caches, list all attributes they bring in
859 extra_info = []
860 for cache_class in self.caches_array:
861 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
862 cachefields = getattr(cache_class, 'cachefields', [])
863 extra_info = extra_info + cachefields
864
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600865 tids = []
866 for mc in taskdata:
867 for tid in taskdata[mc].taskentries:
868 tids.append(tid)
869
870 for tid in tids:
871 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
872
873 pn = self.recipecaches[mc].pkg_fn[taskfn]
874 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500875
876 if pn not in depend_tree["pn"]:
877 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600878 depend_tree["pn"][pn]["filename"] = taskfn
879 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500880 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600881 rdepends = self.recipecaches[mc].rundeps[taskfn]
882 rrecs = self.recipecaches[mc].runrecs[taskfn]
883 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500884
885 # for all extra attributes stored, add them to the dependency tree
886 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600887 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500888
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600889 if taskfn not in seen_fns:
890 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500891
892 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500893 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500894 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600895 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
896 fn_provider = taskdata[mc].build_targets[dep][0]
897 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500898 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500899 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600900 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500901 depend_tree["depends"][pn].append(pn_provider)
902
903 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600904 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500905 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600906 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
907 fn_rprovider = taskdata[mc].run_targets[rdep][0]
908 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500909 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600910 pn_rprovider = rdep
911 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500912 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
913
914 depend_tree["rdepends-pkg"].update(rdepends)
915 depend_tree["rrecs-pkg"].update(rrecs)
916
917 return depend_tree
918
919 def generateDepTreeEvent(self, pkgs_to_build, task):
920 """
921 Create a task dependency graph of pkgs_to_build.
922 Generate an event with the result
923 """
924 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
925 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
926
927 def generateDotGraphFiles(self, pkgs_to_build, task):
928 """
929 Create a task dependency graph of pkgs_to_build.
930 Save the result to a set of .dot files.
931 """
932
933 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
934
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500935 with open('pn-buildlist', 'w') as f:
936 for pn in depgraph["pn"]:
937 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500938 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500939
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500940 # Remove old format output files to ensure no confusion with stale data
941 try:
942 os.unlink('pn-depends.dot')
943 except FileNotFoundError:
944 pass
945 try:
946 os.unlink('package-depends.dot')
947 except FileNotFoundError:
948 pass
Brad Bishop79641f22019-09-10 07:20:22 -0400949 try:
950 os.unlink('recipe-depends.dot')
951 except FileNotFoundError:
952 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500953
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500954 with open('task-depends.dot', 'w') as f:
955 f.write("digraph depends {\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400956 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500957 (pn, taskname) = task.rsplit(".", 1)
958 fn = depgraph["pn"][pn]["filename"]
959 version = depgraph["pn"][pn]["version"]
960 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 -0400961 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500962 f.write('"%s" -> "%s"\n' % (task, dep))
963 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500964 logger.info("Task dependencies saved to 'task-depends.dot'")
965
966 def show_appends_with_no_recipes(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500967 appends_without_recipes = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500968 # Determine which bbappends haven't been applied
Andrew Geissler5a43b432020-06-13 10:46:56 -0500969 for mc in self.multiconfigs:
970 # First get list of recipes, including skipped
971 recipefns = list(self.recipecaches[mc].pkg_fn.keys())
972 recipefns.extend(self.skiplist.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500973
Andrew Geissler5a43b432020-06-13 10:46:56 -0500974 # Work out list of bbappends that have been applied
975 applied_appends = []
976 for fn in recipefns:
977 applied_appends.extend(self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500978
Andrew Geissler5a43b432020-06-13 10:46:56 -0500979 appends_without_recipes[mc] = []
980 for _, appendfn in self.collections[mc].bbappends:
981 if not appendfn in applied_appends:
982 appends_without_recipes[mc].append(appendfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500983
Andrew Geissler5a43b432020-06-13 10:46:56 -0500984 msgs = []
985 for mc in sorted(appends_without_recipes.keys()):
986 if appends_without_recipes[mc]:
987 msgs.append('No recipes in %s available for:\n %s' % (mc if mc else 'default',
988 '\n '.join(appends_without_recipes[mc])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500989
Andrew Geissler5a43b432020-06-13 10:46:56 -0500990 if msgs:
991 msg = "\n".join(msgs)
992 warn_only = self.databuilder.mcdata[mc].getVar("BB_DANGLINGAPPENDS_WARNONLY", \
993 False) or "no"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500994 if warn_only.lower() in ("1", "yes", "true"):
995 bb.warn(msg)
996 else:
997 bb.fatal(msg)
998
999 def handlePrefProviders(self):
1000
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001001 for mc in self.multiconfigs:
1002 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001003 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001004
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001005 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001006 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001007 try:
1008 (providee, provider) = p.split(':')
1009 except:
1010 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
1011 continue
1012 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
1013 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
1014 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001015
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001016 def findConfigFilePath(self, configfile):
1017 """
1018 Find the location on disk of configfile and if it exists and was parsed by BitBake
1019 emit the ConfigFilePathFound event with the path to the file.
1020 """
1021 path = bb.cookerdata.findConfigFile(configfile, self.data)
1022 if not path:
1023 return
1024
1025 # Generate a list of parsed configuration files by searching the files
1026 # listed in the __depends and __base_depends variables with a .conf suffix.
1027 conffiles = []
1028 dep_files = self.data.getVar('__base_depends', False) or []
1029 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
1030
1031 for f in dep_files:
1032 if f[0].endswith(".conf"):
1033 conffiles.append(f[0])
1034
1035 _, conf, conffile = path.rpartition("conf/")
1036 match = os.path.join(conf, conffile)
1037 # Try and find matches for conf/conffilename.conf as we don't always
1038 # have the full path to the file.
1039 for cfg in conffiles:
1040 if cfg.endswith(match):
1041 bb.event.fire(bb.event.ConfigFilePathFound(path),
1042 self.data)
1043 break
1044
1045 def findFilesMatchingInDir(self, filepattern, directory):
1046 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001047 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001048 'directory' in each BBPATH. i.e. to find all rootfs package classes available
1049 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
1050 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001051 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001052 """
1053
1054 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001055 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001056 for path in bbpaths:
1057 dirpath = os.path.join(path, directory)
1058 if os.path.exists(dirpath):
1059 for root, dirs, files in os.walk(dirpath):
1060 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001061 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001062 matches.append(f)
1063
1064 if matches:
1065 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1066
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001067 def findProviders(self, mc=''):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001068 return bb.providers.findProviders(self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001069
1070 def findBestProvider(self, pn, mc=''):
1071 if pn in self.recipecaches[mc].providers:
1072 filenames = self.recipecaches[mc].providers[pn]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001073 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.databuilder.mcdata[mc], self.recipecaches[mc])
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001074 if eligible is not None:
1075 filename = eligible[0]
1076 else:
1077 filename = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001078 return None, None, None, filename
1079 elif pn in self.recipecaches[mc].pkg_pn:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001080 (latest, latest_f, preferred_ver, preferred_file, required) = bb.providers.findBestProvider(pn, self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
1081 if required and preferred_file is None:
1082 return None, None, None, None
1083 return (latest, latest_f, preferred_ver, preferred_file)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001084 else:
1085 return None, None, None, None
1086
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001087 def findConfigFiles(self, varname):
1088 """
1089 Find config files which are appropriate values for varname.
1090 i.e. MACHINE, DISTRO
1091 """
1092 possible = []
1093 var = varname.lower()
1094
1095 data = self.data
1096 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001097 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001098 for path in bbpaths:
1099 confpath = os.path.join(path, "conf", var)
1100 if os.path.exists(confpath):
1101 for root, dirs, files in os.walk(confpath):
1102 # get all child files, these are appropriate values
1103 for f in files:
1104 val, sep, end = f.rpartition('.')
1105 if end == 'conf':
1106 possible.append(val)
1107
1108 if possible:
1109 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1110
1111 def findInheritsClass(self, klass):
1112 """
1113 Find all recipes which inherit the specified class
1114 """
1115 pkg_list = []
1116
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001117 for pfn in self.recipecaches[''].pkg_fn:
1118 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001119 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001120 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001121
1122 return pkg_list
1123
1124 def generateTargetsTree(self, klass=None, pkgs=None):
1125 """
1126 Generate a dependency tree of buildable targets
1127 Generate an event with the result
1128 """
1129 # if the caller hasn't specified a pkgs list default to universe
1130 if not pkgs:
1131 pkgs = ['universe']
1132 # if inherited_class passed ensure all recipes which inherit the
1133 # specified class are included in pkgs
1134 if klass:
1135 extra_pkgs = self.findInheritsClass(klass)
1136 pkgs = pkgs + extra_pkgs
1137
1138 # generate a dependency tree for all our packages
1139 tree = self.generatePkgDepTreeData(pkgs, 'build')
1140 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1141
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001142 def interactiveMode( self ):
1143 """Drop off into a shell"""
1144 try:
1145 from bb import shell
1146 except ImportError:
1147 parselog.exception("Interactive mode not available")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001148 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001149 else:
1150 shell.start( self )
1151
1152
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001153 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001154 """Handle collections"""
1155 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001156 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001157 if collections:
1158 collection_priorities = {}
1159 collection_depends = {}
1160 collection_list = collections.split()
1161 min_prio = 0
1162 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001163 bb.debug(1,'Processing %s in collection list' % (c))
1164
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001165 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001166 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001167 if priority:
1168 try:
1169 prio = int(priority)
1170 except ValueError:
1171 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1172 errors = True
1173 if min_prio == 0 or prio < min_prio:
1174 min_prio = prio
1175 collection_priorities[c] = prio
1176 else:
1177 collection_priorities[c] = None
1178
1179 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001180 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001181 if deps:
1182 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001183 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001184 except bb.utils.VersionStringException as vse:
1185 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001186 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001187 if dep in collection_list:
1188 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001189 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001190 (op, depver) = opstr.split()
1191 if layerver:
1192 try:
1193 res = bb.utils.vercmp_string_op(layerver, depver, op)
1194 except bb.utils.VersionStringException as vse:
1195 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1196 if not res:
1197 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)
1198 errors = True
1199 else:
1200 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)
1201 errors = True
1202 else:
1203 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1204 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001205 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001206 else:
1207 collection_depends[c] = []
1208
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001209 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001210 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001211 if recs:
1212 try:
1213 recDict = bb.utils.explode_dep_versions2(recs)
1214 except bb.utils.VersionStringException as vse:
1215 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1216 for rec, oplist in list(recDict.items()):
1217 if rec in collection_list:
1218 if oplist:
1219 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001220 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001221 if layerver:
1222 (op, recver) = opstr.split()
1223 try:
1224 res = bb.utils.vercmp_string_op(layerver, recver, op)
1225 except bb.utils.VersionStringException as vse:
1226 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1227 if not res:
1228 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)
1229 continue
1230 else:
1231 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)
1232 continue
1233 parselog.debug(3,"Layer '%s' recommends layer '%s', so we are adding it", c, rec)
1234 collection_depends[c].append(rec)
1235 else:
1236 parselog.debug(3,"Layer '%s' recommends layer '%s', but this layer is not enabled in your configuration", c, rec)
1237
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001238 # Recursively work out collection priorities based on dependencies
1239 def calc_layer_priority(collection):
1240 if not collection_priorities[collection]:
1241 max_depprio = min_prio
1242 for dep in collection_depends[collection]:
1243 calc_layer_priority(dep)
1244 depprio = collection_priorities[dep]
1245 if depprio > max_depprio:
1246 max_depprio = depprio
1247 max_depprio += 1
1248 parselog.debug(1, "Calculated priority of layer %s as %d", collection, max_depprio)
1249 collection_priorities[collection] = max_depprio
1250
1251 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1252 for c in collection_list:
1253 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001254 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001255 if regex is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001256 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1257 errors = True
1258 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001259 elif regex == "":
1260 parselog.debug(1, "BBFILE_PATTERN_%s is empty" % c)
Brad Bishop19323692019-04-05 15:28:33 -04001261 cre = re.compile('^NULL$')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001262 errors = False
1263 else:
1264 try:
1265 cre = re.compile(regex)
1266 except re.error:
1267 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1268 errors = True
1269 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001270 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001271 if errors:
1272 # We've already printed the actual error(s)
1273 raise CollectionError("Errors during parsing layer configuration")
1274
1275 def buildSetVars(self):
1276 """
1277 Setup any variables needed before starting a build
1278 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001279 t = time.gmtime()
1280 for mc in self.databuilder.mcdata:
1281 ds = self.databuilder.mcdata[mc]
1282 if not ds.getVar("BUILDNAME", False):
1283 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1284 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1285 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1286 ds.setVar("TIME", time.strftime('%H%M%S', t))
1287
1288 def reset_mtime_caches(self):
1289 """
1290 Reset mtime caches - this is particularly important when memory resident as something
1291 which is cached is not unlikely to have changed since the last invocation (e.g. a
1292 file associated with a recipe might have been modified by the user).
1293 """
1294 build.reset_cache()
1295 bb.fetch._checksum_cache.mtime_cache.clear()
1296 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1297 if siggen_cache:
1298 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001299
Andrew Geissler5a43b432020-06-13 10:46:56 -05001300 def matchFiles(self, bf, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001301 """
1302 Find the .bb files which match the expression in 'buildfile'.
1303 """
1304 if bf.startswith("/") or bf.startswith("../"):
1305 bf = os.path.abspath(bf)
1306
Andrew Geissler5a43b432020-06-13 10:46:56 -05001307 self.collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)}
1308 filelist, masked, searchdirs = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001309 try:
1310 os.stat(bf)
1311 bf = os.path.abspath(bf)
1312 return [bf]
1313 except OSError:
1314 regexp = re.compile(bf)
1315 matches = []
1316 for f in filelist:
1317 if regexp.search(f) and os.path.isfile(f):
1318 matches.append(f)
1319 return matches
1320
Andrew Geissler5a43b432020-06-13 10:46:56 -05001321 def matchFile(self, buildfile, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001322 """
1323 Find the .bb file which matches the expression in 'buildfile'.
1324 Raise an error if multiple files
1325 """
Andrew Geissler5a43b432020-06-13 10:46:56 -05001326 matches = self.matchFiles(buildfile, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001327 if len(matches) != 1:
1328 if matches:
1329 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1330 if matches:
1331 for f in matches:
1332 msg += "\n %s" % f
1333 parselog.error(msg)
1334 else:
1335 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1336 raise NoSpecificMatch
1337 return matches[0]
1338
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001339 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001340 """
1341 Build the file matching regexp buildfile
1342 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001343 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001344
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001345 # Too many people use -b because they think it's how you normally
1346 # specify a target to be built, so show a warning
1347 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1348
1349 self.buildFileInternal(buildfile, task)
1350
1351 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1352 """
1353 Build the file matching regexp buildfile
1354 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001355
1356 # Parse the configuration here. We need to do it explicitly here since
1357 # buildFile() doesn't use the cache
1358 self.parseConfiguration()
1359
1360 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001361 if task is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001362 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001363 if not task.startswith("do_"):
1364 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001365
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001366 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001367 fn = self.matchFile(fn, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001368
1369 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001370 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001371
Andrew Geissler5a43b432020-06-13 10:46:56 -05001372 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001373
Andrew Geissler5a43b432020-06-13 10:46:56 -05001374 infos = bb_caches[mc].parse(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001375 infos = dict(infos)
1376
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001377 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001378 try:
1379 info_array = infos[fn]
1380 except KeyError:
1381 bb.fatal("%s does not exist" % fn)
1382
1383 if info_array[0].skipped:
1384 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1385
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001386 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001387
1388 # Tweak some variables
1389 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001390 self.recipecaches[mc].ignored_dependencies = set()
1391 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001392 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001393
1394 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001395 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1396 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001397 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1398 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001399
1400 # Invalidate task for target if force mode active
1401 if self.configuration.force:
1402 logger.verbose("Invalidate task %s, %s", task, fn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001403 bb.parse.siggen.invalidate_task(task, self.recipecaches[mc], fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001404
1405 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001406 taskdata = {}
1407 taskdata[mc] = bb.taskdata.TaskData(self.configuration.abort)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001408 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001409
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001410 if quietlog:
1411 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1412 bb.runqueue.logger.setLevel(logging.WARNING)
1413
1414 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1415 if fireevents:
1416 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001417
1418 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001419 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001420
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001421 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001422
1423 def buildFileIdle(server, rq, abort):
1424
1425 msg = None
1426 interrupted = 0
1427 if abort or self.state == state.forceshutdown:
1428 rq.finish_runqueue(True)
1429 msg = "Forced shutdown"
1430 interrupted = 2
1431 elif self.state == state.shutdown:
1432 rq.finish_runqueue(False)
1433 msg = "Stopped build"
1434 interrupted = 1
1435 failures = 0
1436 try:
1437 retval = rq.execute_runqueue()
1438 except runqueue.TaskFailure as exc:
1439 failures += len(exc.args)
1440 retval = False
1441 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001442 self.command.finishAsyncCommand(str(exc))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001443 if quietlog:
1444 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001445 return False
1446
1447 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001448 if fireevents:
1449 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 -05001450 self.command.finishAsyncCommand(msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001451 # We trashed self.recipecaches above
1452 self.parsecache_valid = False
1453 self.configuration.limited_deps = False
1454 bb.parse.siggen.reset(self.data)
1455 if quietlog:
1456 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001457 return False
1458 if retval is True:
1459 return True
1460 return retval
1461
Andrew Geissler635e0e42020-08-21 15:58:33 -05001462 self.idleCallBackRegister(buildFileIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001463
1464 def buildTargets(self, targets, task):
1465 """
1466 Attempt to build the targets specified
1467 """
1468
1469 def buildTargetsIdle(server, rq, abort):
1470 msg = None
1471 interrupted = 0
1472 if abort or self.state == state.forceshutdown:
1473 rq.finish_runqueue(True)
1474 msg = "Forced shutdown"
1475 interrupted = 2
1476 elif self.state == state.shutdown:
1477 rq.finish_runqueue(False)
1478 msg = "Stopped build"
1479 interrupted = 1
1480 failures = 0
1481 try:
1482 retval = rq.execute_runqueue()
1483 except runqueue.TaskFailure as exc:
1484 failures += len(exc.args)
1485 retval = False
1486 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001487 self.command.finishAsyncCommand(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001488 return False
1489
1490 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001491 try:
1492 for mc in self.multiconfigs:
1493 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1494 finally:
1495 self.command.finishAsyncCommand(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001496 return False
1497 if retval is True:
1498 return True
1499 return retval
1500
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001501 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001502 self.buildSetVars()
1503
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001504 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001505 if task is None:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001506 task = self.configuration.cmd
1507
1508 if not task.startswith("do_"):
1509 task = "do_%s" % task
1510
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001511 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1512
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001513 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001514
1515 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.abort)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001516
1517 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001518
1519 # make targets to always look as <target>:do_<task>
1520 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001521 for target in runlist:
1522 if target[0]:
Brad Bishop15ae2502019-06-18 21:44:24 -04001523 ntargets.append("mc:%s:%s:%s" % (target[0], target[1], target[2]))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001524 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001525
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001526 for mc in self.multiconfigs:
1527 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001528
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001529 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001530 if 'universe' in targets:
1531 rq.rqdata.warn_multi_bb = True
1532
Andrew Geissler635e0e42020-08-21 15:58:33 -05001533 self.idleCallBackRegister(buildTargetsIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001534
1535
1536 def getAllKeysWithFlags(self, flaglist):
1537 dump = {}
1538 for k in self.data.keys():
1539 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001540 expand = True
1541 flags = self.data.getVarFlags(k)
1542 if flags and "func" in flags and "python" in flags:
1543 expand = False
1544 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001545 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1546 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001547 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001548 'history' : self.data.varhistory.variable(k),
1549 }
1550 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001551 if flags and d in flags:
1552 dump[k][d] = flags[d]
1553 else:
1554 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001555 except Exception as e:
1556 print(e)
1557 return dump
1558
1559
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001560 def updateCacheSync(self):
1561 if self.state == state.running:
1562 return
1563
1564 # reload files for which we got notifications
1565 for p in self.inotify_modified_files:
1566 bb.parse.update_cache(p)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001567 if p in bb.parse.BBHandler.cached_statements:
1568 del bb.parse.BBHandler.cached_statements[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001569 self.inotify_modified_files = []
1570
1571 if not self.baseconfig_valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001572 logger.debug("Reloading base configuration data")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001573 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001574 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001575
1576 # This is called for all async commands when self.state != running
1577 def updateCache(self):
1578 if self.state == state.running:
1579 return
1580
1581 if self.state in (state.shutdown, state.forceshutdown, state.error):
1582 if hasattr(self.parser, 'shutdown'):
1583 self.parser.shutdown(clean=False, force = True)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001584 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001585 raise bb.BBHandledException()
1586
1587 if self.state != state.parsing:
1588 self.updateCacheSync()
1589
1590 if self.state != state.parsing and not self.parsecache_valid:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001591 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001592 self.parseConfiguration ()
1593 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001594 for mc in self.multiconfigs:
1595 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001596
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001597 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001598 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001599 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001600
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001601 for dep in self.configuration.extra_assume_provided:
1602 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001603
Andrew Geissler5a43b432020-06-13 10:46:56 -05001604 self.collections = {}
1605
1606 mcfilelist = {}
1607 total_masked = 0
1608 searchdirs = set()
1609 for mc in self.multiconfigs:
1610 self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc)
1611 (filelist, masked, search) = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
1612
1613 mcfilelist[mc] = filelist
1614 total_masked += masked
1615 searchdirs |= set(search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001616
1617 # Add inotify watches for directories searched for bb/bbappend files
1618 for dirent in searchdirs:
1619 self.add_filewatch([[dirent]], dirs=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001620
Andrew Geissler5a43b432020-06-13 10:46:56 -05001621 self.parser = CookerParser(self, mcfilelist, total_masked)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001622 self.parsecache_valid = True
1623
1624 self.state = state.parsing
1625
1626 if not self.parser.parse_next():
1627 collectlog.debug(1, "parsing complete")
1628 if self.parser.error:
1629 raise bb.BBHandledException()
1630 self.show_appends_with_no_recipes()
1631 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001632 for mc in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001633 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 -05001634 self.state = state.running
1635
1636 # Send an event listing all stamps reachable after parsing
1637 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001638 for mc in self.multiconfigs:
1639 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1640 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001641 return None
1642
1643 return True
1644
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001645 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001646
1647 # Return a copy, don't modify the original
1648 pkgs_to_build = pkgs_to_build[:]
1649
1650 if len(pkgs_to_build) == 0:
1651 raise NothingToBuild
1652
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001653 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001654 for pkg in pkgs_to_build.copy():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001655 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001656 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Brad Bishop15ae2502019-06-18 21:44:24 -04001657 if pkg.startswith("multiconfig:"):
1658 pkgs_to_build.remove(pkg)
1659 pkgs_to_build.append(pkg.replace("multiconfig:", "mc:"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001660
1661 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001662 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001663 for mc in self.multiconfigs:
1664 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1665 for t in self.recipecaches[mc].world_target:
1666 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001667 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001668 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001669
1670 if 'universe' in pkgs_to_build:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001671 parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001672 parselog.debug(1, "collating packages for \"universe\"")
1673 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001674 for mc in self.multiconfigs:
1675 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001676 if task:
1677 foundtask = False
1678 for provider_fn in self.recipecaches[mc].providers[t]:
1679 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1680 foundtask = True
1681 break
1682 if not foundtask:
1683 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1684 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001685 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001686 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001687 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001688
1689 return pkgs_to_build
1690
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001691 def pre_serve(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001692 return
1693
1694 def post_serve(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001695 self.shutdown(force=True)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001696 prserv.serv.auto_shutdown()
Brad Bishop08902b02019-08-20 09:16:51 -04001697 if self.hashserv:
1698 self.hashserv.process.terminate()
1699 self.hashserv.process.join()
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001700 if hasattr(self, "data"):
1701 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001702
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001703 def shutdown(self, force = False):
1704 if force:
1705 self.state = state.forceshutdown
1706 else:
1707 self.state = state.shutdown
1708
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001709 if self.parser:
1710 self.parser.shutdown(clean=not force, force=force)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001711 self.parser.final_cleanup()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001712
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001713 def finishcommand(self):
1714 self.state = state.initial
1715
1716 def reset(self):
1717 self.initConfigurationData()
Brad Bishop08902b02019-08-20 09:16:51 -04001718 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001719
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001720 def clientComplete(self):
1721 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001722 self.finishcommand()
1723 self.extraconfigdata = {}
1724 self.command.reset()
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001725 if hasattr(self, "data"):
1726 self.databuilder.reset()
1727 self.data = self.databuilder.data
Andrew Geissler82c905d2020-04-13 13:39:40 -05001728 self.parsecache_valid = False
1729 self.baseconfig_valid = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001730
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001731
1732class CookerExit(bb.event.Event):
1733 """
1734 Notify clients of the Cooker shutdown
1735 """
1736
1737 def __init__(self):
1738 bb.event.Event.__init__(self)
1739
1740
1741class CookerCollectFiles(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001742 def __init__(self, priorities, mc=''):
1743 self.mc = mc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001744 self.bbappends = []
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001745 # Priorities is a list of tupples, with the second element as the pattern.
1746 # We need to sort the list with the longest pattern first, and so on to
1747 # the shortest. This allows nested layers to be properly evaluated.
1748 self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001749
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001750 def calc_bbfile_priority(self, filename):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001751 for _, _, regex, pri in self.bbfile_config_priorities:
1752 if regex.match(filename):
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001753 return pri, regex
1754 return 0, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001755
1756 def get_bbfiles(self):
1757 """Get list of default .bb files by reading out the current directory"""
1758 path = os.getcwd()
1759 contents = os.listdir(path)
1760 bbfiles = []
1761 for f in contents:
1762 if f.endswith(".bb"):
1763 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1764 return bbfiles
1765
1766 def find_bbfiles(self, path):
1767 """Find all the .bb and .bbappend files in a directory"""
1768 found = []
1769 for dir, dirs, files in os.walk(path):
1770 for ignored in ('SCCS', 'CVS', '.svn'):
1771 if ignored in dirs:
1772 dirs.remove(ignored)
1773 found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
1774
1775 return found
1776
1777 def collect_bbfiles(self, config, eventdata):
1778 """Collect all available .bb build files"""
1779 masked = 0
1780
1781 collectlog.debug(1, "collecting .bb files")
1782
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001783 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001784
1785 # Sort files by priority
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001786 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem)[0] )
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001787 config.setVar("BBFILES_PRIORITIZED", " ".join(files))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001788
1789 if not len(files):
1790 files = self.get_bbfiles()
1791
1792 if not len(files):
1793 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1794 bb.event.fire(CookerExit(), eventdata)
1795
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001796 # We need to track where we look so that we can add inotify watches. There
1797 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001798 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001799 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001800 if hasattr(os, 'scandir'):
1801 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001802 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001803
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001804 def ourlistdir(d):
1805 searchdirs.append(d)
1806 return origlistdir(d)
1807
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001808 def ourscandir(d):
1809 searchdirs.append(d)
1810 return origscandir(d)
1811
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001812 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001813 if hasattr(os, 'scandir'):
1814 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001815 try:
1816 # Can't use set here as order is important
1817 newfiles = []
1818 for f in files:
1819 if os.path.isdir(f):
1820 dirfiles = self.find_bbfiles(f)
1821 for g in dirfiles:
1822 if g not in newfiles:
1823 newfiles.append(g)
1824 else:
1825 globbed = glob.glob(f)
1826 if not globbed and os.path.exists(f):
1827 globbed = [f]
1828 # glob gives files in order on disk. Sort to be deterministic.
1829 for g in sorted(globbed):
1830 if g not in newfiles:
1831 newfiles.append(g)
1832 finally:
1833 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001834 if hasattr(os, 'scandir'):
1835 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001836
1837 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001838
1839 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001840 # First validate the individual regular expressions and ignore any
1841 # that do not compile
1842 bbmasks = []
1843 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001844 # When constructing an older style single regex, it's possible for BBMASK
1845 # to end up beginning with '|', which matches and masks _everything_.
1846 if mask.startswith("|"):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001847 collectlog.warning("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001848 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001849 try:
1850 re.compile(mask)
1851 bbmasks.append(mask)
1852 except sre_constants.error:
1853 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1854
1855 # Then validate the combined regular expressions. This should never
1856 # fail, but better safe than sorry...
1857 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001858 try:
1859 bbmask_compiled = re.compile(bbmask)
1860 except sre_constants.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001861 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1862 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001863
1864 bbfiles = []
1865 bbappend = []
1866 for f in newfiles:
1867 if bbmask and bbmask_compiled.search(f):
1868 collectlog.debug(1, "skipping masked file %s", f)
1869 masked += 1
1870 continue
1871 if f.endswith('.bb'):
1872 bbfiles.append(f)
1873 elif f.endswith('.bbappend'):
1874 bbappend.append(f)
1875 else:
1876 collectlog.debug(1, "skipping %s: unknown file extension", f)
1877
1878 # Build a list of .bbappend files for each .bb file
1879 for f in bbappend:
1880 base = os.path.basename(f).replace('.bbappend', '.bb')
1881 self.bbappends.append((base, f))
1882
1883 # Find overlayed recipes
1884 # bbfiles will be in priority order which makes this easy
1885 bbfile_seen = dict()
1886 self.overlayed = defaultdict(list)
1887 for f in reversed(bbfiles):
1888 base = os.path.basename(f)
1889 if base not in bbfile_seen:
1890 bbfile_seen[base] = f
1891 else:
1892 topfile = bbfile_seen[base]
1893 self.overlayed[topfile].append(f)
1894
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001895 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001896
1897 def get_file_appends(self, fn):
1898 """
1899 Returns a list of .bbappend files to apply to fn
1900 """
1901 filelist = []
1902 f = os.path.basename(fn)
1903 for b in self.bbappends:
1904 (bbappend, filename) = b
1905 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1906 filelist.append(filename)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001907 return tuple(filelist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001908
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001909 def collection_priorities(self, pkgfns, fns, d):
1910 # Return the priorities of the entries in pkgfns
1911 # Also check that all the regexes in self.bbfile_config_priorities are used
1912 # (but to do that we need to ensure skipped recipes aren't counted, nor
1913 # collections in BBFILE_PATTERN_IGNORE_EMPTY)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001914
1915 priorities = {}
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001916 seen = set()
1917 matched = set()
1918
1919 matched_regex = set()
1920 unmatched_regex = set()
1921 for _, _, regex, _ in self.bbfile_config_priorities:
1922 unmatched_regex.add(regex)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001923
1924 # Calculate priorities for each file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001925 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001926 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001927 priorities[p], regex = self.calc_bbfile_priority(realfn)
1928 if regex in unmatched_regex:
1929 matched_regex.add(regex)
1930 unmatched_regex.remove(regex)
1931 seen.add(realfn)
1932 if regex:
1933 matched.add(realfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001934
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001935 if unmatched_regex:
1936 # Account for bbappend files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001937 for b in self.bbappends:
1938 (bbfile, append) = b
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001939 seen.add(append)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001940
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001941 # Account for skipped recipes
1942 seen.update(fns)
1943
1944 seen.difference_update(matched)
1945
1946 def already_matched(fn):
1947 for regex in matched_regex:
1948 if regex.match(fn):
1949 return True
1950 return False
1951
1952 for unmatch in unmatched_regex.copy():
1953 for fn in seen:
1954 if unmatch.match(fn):
1955 # If the bbappend or file was already matched by another regex, skip it
1956 # e.g. for a layer within a layer, the outer regex could match, the inner
1957 # regex may match nothing and we should warn about that
1958 if already_matched(fn):
1959 continue
1960 unmatched_regex.remove(unmatch)
1961 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001962
1963 for collection, pattern, regex, _ in self.bbfile_config_priorities:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001964 if regex in unmatched_regex:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001965 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Andrew Geissler5a43b432020-06-13 10:46:56 -05001966 collectlog.warning("No bb files in %s matched BBFILE_PATTERN_%s '%s'" % (self.mc if self.mc else 'default',
1967 collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001968
1969 return priorities
1970
1971class ParsingFailure(Exception):
1972 def __init__(self, realexception, recipe):
1973 self.realexception = realexception
1974 self.recipe = recipe
1975 Exception.__init__(self, realexception, recipe)
1976
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001977class Parser(multiprocessing.Process):
1978 def __init__(self, jobs, results, quit, init, profile):
1979 self.jobs = jobs
1980 self.results = results
1981 self.quit = quit
1982 self.init = init
1983 multiprocessing.Process.__init__(self)
1984 self.context = bb.utils.get_context().copy()
1985 self.handlers = bb.event.get_class_handlers().copy()
1986 self.profile = profile
1987
1988 def run(self):
1989
1990 if not self.profile:
1991 self.realrun()
1992 return
1993
1994 try:
1995 import cProfile as profile
1996 except:
1997 import profile
1998 prof = profile.Profile()
1999 try:
2000 profile.Profile.runcall(prof, self.realrun)
2001 finally:
2002 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
2003 prof.dump_stats(logfile)
2004
2005 def realrun(self):
2006 if self.init:
2007 self.init()
2008
2009 pending = []
2010 while True:
2011 try:
2012 self.quit.get_nowait()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002013 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002014 pass
2015 else:
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002016 self.results.close()
2017 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002018 break
2019
2020 if pending:
2021 result = pending.pop()
2022 else:
2023 try:
Brad Bishop19323692019-04-05 15:28:33 -04002024 job = self.jobs.pop()
2025 except IndexError:
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002026 self.results.close()
2027 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002028 break
2029 result = self.parse(*job)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002030 # Clear the siggen cache after parsing to control memory usage, its huge
2031 bb.parse.siggen.postparsing_clean_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002032 try:
2033 self.results.put(result, timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002034 except queue.Full:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002035 pending.append(result)
2036
Andrew Geissler5a43b432020-06-13 10:46:56 -05002037 def parse(self, mc, cache, filename, appends):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002038 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002039 origfilter = bb.event.LogHandler.filter
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002040 # Record the filename we're parsing into any events generated
2041 def parse_filter(self, record):
2042 record.taskpid = bb.event.worker_pid
2043 record.fn = filename
2044 return True
2045
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002046 # Reset our environment and handlers to the original settings
2047 bb.utils.set_context(self.context.copy())
2048 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002049 bb.event.LogHandler.filter = parse_filter
2050
Andrew Geissler5a43b432020-06-13 10:46:56 -05002051 return True, mc, cache.parse(filename, appends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002052 except Exception as exc:
2053 tb = sys.exc_info()[2]
2054 exc.recipe = filename
2055 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
2056 return True, exc
2057 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
2058 # and for example a worker thread doesn't just exit on its own in response to
2059 # a SystemExit event for example.
2060 except BaseException as exc:
2061 return True, ParsingFailure(exc, filename)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002062 finally:
2063 bb.event.LogHandler.filter = origfilter
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002064
2065class CookerParser(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002066 def __init__(self, cooker, mcfilelist, masked):
2067 self.mcfilelist = mcfilelist
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002068 self.cooker = cooker
2069 self.cfgdata = cooker.data
2070 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002071 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002072
2073 # Accounting statistics
2074 self.parsed = 0
2075 self.cached = 0
2076 self.error = 0
2077 self.masked = masked
2078
2079 self.skipped = 0
2080 self.virtuals = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002081
2082 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002083 self.process_names = []
2084
Andrew Geissler5a43b432020-06-13 10:46:56 -05002085 self.bb_caches = bb.cache.MulticonfigCache(self.cfgbuilder, self.cfghash, cooker.caches_array)
2086 self.fromcache = set()
2087 self.willparse = set()
2088 for mc in self.cooker.multiconfigs:
2089 for filename in self.mcfilelist[mc]:
2090 appends = self.cooker.collections[mc].get_file_appends(filename)
2091 if not self.bb_caches[mc].cacheValid(filename, appends):
2092 self.willparse.add((mc, self.bb_caches[mc], filename, appends))
2093 else:
2094 self.fromcache.add((mc, self.bb_caches[mc], filename, appends))
2095
2096 self.total = len(self.fromcache) + len(self.willparse)
2097 self.toparse = len(self.willparse)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002098 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002099
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002100 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Andrew Geissler5a43b432020-06-13 10:46:56 -05002101 multiprocessing.cpu_count()), self.toparse)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002102
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002103 self.start()
2104 self.haveshutdown = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002105 self.syncthread = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002106
2107 def start(self):
2108 self.results = self.load_cached()
2109 self.processes = []
2110 if self.toparse:
2111 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
2112 def init():
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002113 signal.signal(signal.SIGTERM, signal.SIG_DFL)
2114 signal.signal(signal.SIGHUP, signal.SIG_DFL)
2115 signal.signal(signal.SIGINT, signal.SIG_IGN)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002116 bb.utils.set_process_name(multiprocessing.current_process().name)
2117 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2118 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002119
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002120 self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002121 self.result_queue = multiprocessing.Queue()
Brad Bishop19323692019-04-05 15:28:33 -04002122
2123 def chunkify(lst,n):
2124 return [lst[i::n] for i in range(n)]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002125 self.jobs = chunkify(list(self.willparse), self.num_processes)
Brad Bishop19323692019-04-05 15:28:33 -04002126
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002127 for i in range(0, self.num_processes):
Brad Bishop19323692019-04-05 15:28:33 -04002128 parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, init, self.cooker.configuration.profile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002129 parser.start()
2130 self.process_names.append(parser.name)
2131 self.processes.append(parser)
2132
2133 self.results = itertools.chain(self.results, self.parse_generator())
2134
2135 def shutdown(self, clean=True, force=False):
2136 if not self.toparse:
2137 return
2138 if self.haveshutdown:
2139 return
2140 self.haveshutdown = True
2141
2142 if clean:
2143 event = bb.event.ParseCompleted(self.cached, self.parsed,
2144 self.skipped, self.masked,
2145 self.virtuals, self.error,
2146 self.total)
2147
2148 bb.event.fire(event, self.cfgdata)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002149
2150 for process in self.processes:
2151 self.parser_quit.put(None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002152
Brad Bishop08902b02019-08-20 09:16:51 -04002153 # Cleanup the queue before call process.join(), otherwise there might be
2154 # deadlocks.
2155 while True:
2156 try:
2157 self.result_queue.get(timeout=0.25)
2158 except queue.Empty:
2159 break
2160
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002161 for process in self.processes:
2162 if force:
2163 process.join(.1)
2164 process.terminate()
2165 else:
2166 process.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002167
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002168 self.parser_quit.close()
2169 # Allow data left in the cancel queue to be discarded
2170 self.parser_quit.cancel_join_thread()
2171
Andrew Geissler5a43b432020-06-13 10:46:56 -05002172 def sync_caches():
2173 for c in self.bb_caches.values():
2174 c.sync()
2175
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002176 sync = threading.Thread(target=sync_caches, name="SyncThread")
2177 self.syncthread = sync
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002178 sync.start()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002179 bb.codeparser.parser_cache_savemerge()
2180 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002181 if self.cooker.configuration.profile:
2182 profiles = []
2183 for i in self.process_names:
2184 logfile = "profile-parse-%s.log" % i
2185 if os.path.exists(logfile):
2186 profiles.append(logfile)
2187
2188 pout = "profile-parse.log.processed"
2189 bb.utils.process_profilelog(profiles, pout = pout)
2190 print("Processed parsing statistics saved to %s" % (pout))
2191
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002192 def final_cleanup(self):
2193 if self.syncthread:
2194 self.syncthread.join()
2195
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002196 def load_cached(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002197 for mc, cache, filename, appends in self.fromcache:
2198 cached, infos = cache.load(filename, appends)
2199 yield not cached, mc, infos
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002200
2201 def parse_generator(self):
2202 while True:
2203 if self.parsed >= self.toparse:
2204 break
2205
2206 try:
2207 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002208 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002209 pass
2210 else:
2211 value = result[1]
2212 if isinstance(value, BaseException):
2213 raise value
2214 else:
2215 yield result
2216
2217 def parse_next(self):
2218 result = []
2219 parsed = None
2220 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002221 parsed, mc, result = next(self.results)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002222 except StopIteration:
2223 self.shutdown()
2224 return False
2225 except bb.BBHandledException as exc:
2226 self.error += 1
2227 logger.error('Failed to parse recipe: %s' % exc.recipe)
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002228 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002229 return False
2230 except ParsingFailure as exc:
2231 self.error += 1
2232 logger.error('Unable to parse %s: %s' %
2233 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002234 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002235 return False
2236 except bb.parse.ParseError as exc:
2237 self.error += 1
2238 logger.error(str(exc))
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002239 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002240 return False
2241 except bb.data_smart.ExpansionError as exc:
2242 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002243 bbdir = os.path.dirname(__file__) + os.sep
2244 etype, value, _ = sys.exc_info()
2245 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2246 logger.error('ExpansionError during parsing %s', value.recipe,
2247 exc_info=(etype, value, tb))
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002248 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002249 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002250 except Exception as exc:
2251 self.error += 1
2252 etype, value, tb = sys.exc_info()
2253 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002254 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002255 exc_info=(etype, value, exc.traceback))
2256 else:
2257 # Most likely, an exception occurred during raising an exception
2258 import traceback
2259 logger.error('Exception during parse: %s' % traceback.format_exc())
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002260 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002261 return False
2262
2263 self.current += 1
2264 self.virtuals += len(result)
2265 if parsed:
2266 self.parsed += 1
2267 if self.parsed % self.progress_chunk == 0:
2268 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2269 self.cfgdata)
2270 else:
2271 self.cached += 1
2272
2273 for virtualfn, info_array in result:
2274 if info_array[0].skipped:
2275 self.skipped += 1
2276 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Andrew Geissler5a43b432020-06-13 10:46:56 -05002277 self.bb_caches[mc].add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002278 parsed=parsed, watcher = self.cooker.add_filewatch)
2279 return True
2280
2281 def reparse(self, filename):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002282 to_reparse = set()
2283 for mc in self.cooker.multiconfigs:
2284 to_reparse.add((mc, filename, self.cooker.collections[mc].get_file_appends(filename)))
2285
2286 for mc, filename, appends in to_reparse:
2287 infos = self.bb_caches[mc].parse(filename, appends)
2288 for vfn, info_array in infos:
2289 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)