blob: 0e492b9be9e6489c9df75cb552a7f65cc18424e3 [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
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500505 (latest_versions, preferred_versions) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500506
507 logger.plain("%-35s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version")
508 logger.plain("%-35s %25s %25s\n", "===========", "==============", "=================")
509
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500510 for p in sorted(self.recipecaches[''].pkg_pn):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500511 pref = preferred_versions[p]
512 latest = latest_versions[p]
513
514 prefstr = pref[0][0] + ":" + pref[0][1] + '-' + pref[0][2]
515 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
516
517 if pref == latest:
518 prefstr = ""
519
520 logger.plain("%-35s %25s %25s", p, lateststr, prefstr)
521
522 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
523 """
524 Show the outer or per-recipe environment
525 """
526 fn = None
527 envdata = None
Brad Bishop15ae2502019-06-18 21:44:24 -0400528 mc = ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500529 if not pkgs_to_build:
530 pkgs_to_build = []
531
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500532 orig_tracking = self.configuration.tracking
533 if not orig_tracking:
534 self.enableDataTracking()
535 self.reset()
536
Brad Bishop15ae2502019-06-18 21:44:24 -0400537 def mc_base(p):
538 if p.startswith('mc:'):
539 s = p.split(':')
540 if len(s) == 2:
541 return s[1]
542 return None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500543
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500544 if buildfile:
545 # Parse the configuration here. We need to do it explicitly here since
546 # this showEnvironment() code path doesn't use the cache
547 self.parseConfiguration()
548
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600549 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500550 fn = self.matchFile(fn, mc)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600551 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500552 elif len(pkgs_to_build) == 1:
Brad Bishop15ae2502019-06-18 21:44:24 -0400553 mc = mc_base(pkgs_to_build[0])
554 if not mc:
555 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
556 if pkgs_to_build[0] in set(ignore.split()):
557 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500558
Brad Bishop15ae2502019-06-18 21:44:24 -0400559 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.abort, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500560
Brad Bishop15ae2502019-06-18 21:44:24 -0400561 mc = runlist[0][0]
562 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500563
564 if fn:
565 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -0500566 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
567 envdata = bb_caches[mc].loadDataFull(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500568 except Exception as e:
569 parselog.exception("Unable to read %s", fn)
570 raise
Brad Bishop15ae2502019-06-18 21:44:24 -0400571 else:
572 if not mc in self.databuilder.mcdata:
573 bb.fatal('Not multiconfig named "%s" found' % mc)
574 envdata = self.databuilder.mcdata[mc]
575 data.expandKeys(envdata)
576 parse.ast.runAnonFuncs(envdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500577
578 # Display history
579 with closing(StringIO()) as env:
580 self.data.inchistory.emit(env)
581 logger.plain(env.getvalue())
582
583 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500584 with closing(StringIO()) as env:
585 data.emit_env(env, envdata, True)
586 logger.plain(env.getvalue())
587
588 # emit the metadata which isnt valid shell
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500589 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600590 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500591 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500592
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500593 if not orig_tracking:
594 self.disableDataTracking()
595 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500596
597 def buildTaskData(self, pkgs_to_build, task, abort, allowincomplete=False):
598 """
599 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
600 """
601 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
602
603 # A task of None means use the default task
604 if task is None:
605 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500606 if not task.startswith("do_"):
607 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500608
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500609 targetlist = self.checkPackages(pkgs_to_build, task)
610 fulltargetlist = []
611 defaulttask_implicit = ''
612 defaulttask_explicit = False
613 wildcard = False
614
615 # Wild card expansion:
Brad Bishop15ae2502019-06-18 21:44:24 -0400616 # Replace string such as "mc:*:bash"
617 # into "mc:A:bash mc:B:bash bash"
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500618 for k in targetlist:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600619 if k.startswith("mc:") and k.count(':') >= 2:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500620 if wildcard:
621 bb.fatal('multiconfig conflict')
622 if k.split(":")[1] == "*":
623 wildcard = True
624 for mc in self.multiconfigs:
625 if mc:
626 fulltargetlist.append(k.replace('*', mc))
627 # implicit default task
628 else:
629 defaulttask_implicit = k.split(":")[2]
630 else:
631 fulltargetlist.append(k)
632 else:
633 defaulttask_explicit = True
634 fulltargetlist.append(k)
635
636 if not defaulttask_explicit and defaulttask_implicit != '':
637 fulltargetlist.append(defaulttask_implicit)
638
639 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600640 taskdata = {}
641 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500642
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600643 for mc in self.multiconfigs:
644 taskdata[mc] = bb.taskdata.TaskData(abort, skiplist=self.skiplist, allowincomplete=allowincomplete)
645 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600646 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500647
648 current = 0
649 runlist = []
650 for k in fulltargetlist:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500651 origk = k
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600652 mc = ""
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600653 if k.startswith("mc:") and k.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600654 mc = k.split(":")[1]
655 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656 ktask = task
657 if ":do_" in k:
658 k2 = k.split(":do_")
659 k = k2[0]
660 ktask = k2[1]
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500661
662 if mc not in self.multiconfigs:
663 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named %s" % (origk, mc))
664
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600665 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500666 current += 1
667 if not ktask.startswith("do_"):
668 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600669 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
670 # e.g. in ASSUME_PROVIDED
671 continue
672 fn = taskdata[mc].build_targets[k][0]
673 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500674 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600675
Brad Bishop15ae2502019-06-18 21:44:24 -0400676 havemc = False
677 for mc in self.multiconfigs:
678 if taskdata[mc].get_mcdepends():
679 havemc = True
Brad Bishopf058f492019-01-28 23:50:33 -0500680
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800681 # No need to do check providers if there are no mcdeps or not an mc build
Brad Bishop15ae2502019-06-18 21:44:24 -0400682 if havemc or len(self.multiconfigs) > 1:
Andrew Geissler99467da2019-02-25 18:54:23 -0600683 seen = set()
684 new = True
685 # Make sure we can provide the multiconfig dependency
686 while new:
687 mcdeps = set()
688 # Add unresolved first, so we can get multiconfig indirect dependencies on time
689 for mc in self.multiconfigs:
690 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
691 mcdeps |= set(taskdata[mc].get_mcdepends())
692 new = False
693 for mc in self.multiconfigs:
694 for k in mcdeps:
695 if k in seen:
696 continue
697 l = k.split(':')
698 depmc = l[2]
699 if depmc not in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500700 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named configuration %s" % (k,depmc))
Andrew Geissler99467da2019-02-25 18:54:23 -0600701 else:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600702 logger.debug("Adding providers for multiconfig dependency %s" % l[3])
Andrew Geissler99467da2019-02-25 18:54:23 -0600703 taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
704 seen.add(k)
705 new = True
Brad Bishopf058f492019-01-28 23:50:33 -0500706
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600707 for mc in self.multiconfigs:
708 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
709
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600711 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500712
713 def prepareTreeData(self, pkgs_to_build, task):
714 """
715 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
716 """
717
718 # We set abort to False here to prevent unbuildable targets raising
719 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600720 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500721
722 return runlist, taskdata
723
724 ######## WARNING : this function requires cache_extra to be enabled ########
725
726 def generateTaskDepTreeData(self, pkgs_to_build, task):
727 """
728 Create a dependency graph of pkgs_to_build including reverse dependency
729 information.
730 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500731 if not task.startswith("do_"):
732 task = "do_%s" % task
733
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500734 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600735 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736 rq.rqdata.prepare()
737 return self.buildDependTree(rq, taskdata)
738
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600739 @staticmethod
740 def add_mc_prefix(mc, pn):
741 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -0400742 return "mc:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600743 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500744
745 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600746 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500747 depend_tree = {}
748 depend_tree["depends"] = {}
749 depend_tree["tdepends"] = {}
750 depend_tree["pn"] = {}
751 depend_tree["rdepends-pn"] = {}
752 depend_tree["packages"] = {}
753 depend_tree["rdepends-pkg"] = {}
754 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500755 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600756 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500757
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600758 for mc in taskdata:
759 for name, fn in list(taskdata[mc].get_providermap().items()):
760 pn = self.recipecaches[mc].pkg_fn[fn]
761 pn = self.add_mc_prefix(mc, pn)
762 if name != pn:
763 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
764 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500765
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600766 for tid in rq.rqdata.runtaskentries:
767 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
768 pn = self.recipecaches[mc].pkg_fn[taskfn]
769 pn = self.add_mc_prefix(mc, pn)
770 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771 if pn not in depend_tree["pn"]:
772 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600773 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600775 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500776
777 # if we have extra caches, list all attributes they bring in
778 extra_info = []
779 for cache_class in self.caches_array:
780 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
781 cachefields = getattr(cache_class, 'cachefields', [])
782 extra_info = extra_info + cachefields
783
784 # for all attributes stored, add them to the dependency tree
785 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600786 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787
788
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500789 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
790 if not dotname in depend_tree["tdepends"]:
791 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600792 for dep in rq.rqdata.runtaskentries[tid].depends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800793 (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
794 deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600795 depend_tree["tdepends"][dotname].append("%s.%s" % (deppn, bb.runqueue.taskname_from_tid(dep)))
796 if taskfn not in seen_fns:
797 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500798 packages = []
799
800 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600801 for dep in taskdata[mc].depids[taskfn]:
802 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500803
804 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600805 for rdep in taskdata[mc].rdepids[taskfn]:
806 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500807
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600808 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500809 for package in rdepends:
810 depend_tree["rdepends-pkg"][package] = []
811 for rdepend in rdepends[package]:
812 depend_tree["rdepends-pkg"][package].append(rdepend)
813 packages.append(package)
814
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600815 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500816 for package in rrecs:
817 depend_tree["rrecs-pkg"][package] = []
818 for rdepend in rrecs[package]:
819 depend_tree["rrecs-pkg"][package].append(rdepend)
820 if not package in packages:
821 packages.append(package)
822
823 for package in packages:
824 if package not in depend_tree["packages"]:
825 depend_tree["packages"][package] = {}
826 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600827 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500828 depend_tree["packages"][package]["version"] = version
829
830 return depend_tree
831
832 ######## WARNING : this function requires cache_extra to be enabled ########
833 def generatePkgDepTreeData(self, pkgs_to_build, task):
834 """
835 Create a dependency tree of pkgs_to_build, returning the data.
836 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500837 if not task.startswith("do_"):
838 task = "do_%s" % task
839
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500840 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500841
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600842 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500843 depend_tree = {}
844 depend_tree["depends"] = {}
845 depend_tree["pn"] = {}
846 depend_tree["rdepends-pn"] = {}
847 depend_tree["rdepends-pkg"] = {}
848 depend_tree["rrecs-pkg"] = {}
849
850 # if we have extra caches, list all attributes they bring in
851 extra_info = []
852 for cache_class in self.caches_array:
853 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
854 cachefields = getattr(cache_class, 'cachefields', [])
855 extra_info = extra_info + cachefields
856
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600857 tids = []
858 for mc in taskdata:
859 for tid in taskdata[mc].taskentries:
860 tids.append(tid)
861
862 for tid in tids:
863 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
864
865 pn = self.recipecaches[mc].pkg_fn[taskfn]
866 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500867
868 if pn not in depend_tree["pn"]:
869 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600870 depend_tree["pn"][pn]["filename"] = taskfn
871 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500872 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600873 rdepends = self.recipecaches[mc].rundeps[taskfn]
874 rrecs = self.recipecaches[mc].runrecs[taskfn]
875 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500876
877 # for all extra attributes stored, add them to the dependency tree
878 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600879 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500880
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600881 if taskfn not in seen_fns:
882 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500883
884 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500885 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500886 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600887 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
888 fn_provider = taskdata[mc].build_targets[dep][0]
889 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500890 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500891 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600892 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500893 depend_tree["depends"][pn].append(pn_provider)
894
895 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600896 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500897 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600898 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
899 fn_rprovider = taskdata[mc].run_targets[rdep][0]
900 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500901 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600902 pn_rprovider = rdep
903 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500904 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
905
906 depend_tree["rdepends-pkg"].update(rdepends)
907 depend_tree["rrecs-pkg"].update(rrecs)
908
909 return depend_tree
910
911 def generateDepTreeEvent(self, pkgs_to_build, task):
912 """
913 Create a task dependency graph of pkgs_to_build.
914 Generate an event with the result
915 """
916 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
917 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
918
919 def generateDotGraphFiles(self, pkgs_to_build, task):
920 """
921 Create a task dependency graph of pkgs_to_build.
922 Save the result to a set of .dot files.
923 """
924
925 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
926
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500927 with open('pn-buildlist', 'w') as f:
928 for pn in depgraph["pn"]:
929 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500930 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500931
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500932 # Remove old format output files to ensure no confusion with stale data
933 try:
934 os.unlink('pn-depends.dot')
935 except FileNotFoundError:
936 pass
937 try:
938 os.unlink('package-depends.dot')
939 except FileNotFoundError:
940 pass
Brad Bishop79641f22019-09-10 07:20:22 -0400941 try:
942 os.unlink('recipe-depends.dot')
943 except FileNotFoundError:
944 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500945
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500946 with open('task-depends.dot', 'w') as f:
947 f.write("digraph depends {\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400948 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500949 (pn, taskname) = task.rsplit(".", 1)
950 fn = depgraph["pn"][pn]["filename"]
951 version = depgraph["pn"][pn]["version"]
952 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 -0400953 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500954 f.write('"%s" -> "%s"\n' % (task, dep))
955 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500956 logger.info("Task dependencies saved to 'task-depends.dot'")
957
958 def show_appends_with_no_recipes(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500959 appends_without_recipes = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500960 # Determine which bbappends haven't been applied
Andrew Geissler5a43b432020-06-13 10:46:56 -0500961 for mc in self.multiconfigs:
962 # First get list of recipes, including skipped
963 recipefns = list(self.recipecaches[mc].pkg_fn.keys())
964 recipefns.extend(self.skiplist.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500965
Andrew Geissler5a43b432020-06-13 10:46:56 -0500966 # Work out list of bbappends that have been applied
967 applied_appends = []
968 for fn in recipefns:
969 applied_appends.extend(self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500970
Andrew Geissler5a43b432020-06-13 10:46:56 -0500971 appends_without_recipes[mc] = []
972 for _, appendfn in self.collections[mc].bbappends:
973 if not appendfn in applied_appends:
974 appends_without_recipes[mc].append(appendfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500975
Andrew Geissler5a43b432020-06-13 10:46:56 -0500976 msgs = []
977 for mc in sorted(appends_without_recipes.keys()):
978 if appends_without_recipes[mc]:
979 msgs.append('No recipes in %s available for:\n %s' % (mc if mc else 'default',
980 '\n '.join(appends_without_recipes[mc])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500981
Andrew Geissler5a43b432020-06-13 10:46:56 -0500982 if msgs:
983 msg = "\n".join(msgs)
984 warn_only = self.databuilder.mcdata[mc].getVar("BB_DANGLINGAPPENDS_WARNONLY", \
985 False) or "no"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500986 if warn_only.lower() in ("1", "yes", "true"):
987 bb.warn(msg)
988 else:
989 bb.fatal(msg)
990
991 def handlePrefProviders(self):
992
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600993 for mc in self.multiconfigs:
994 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600995 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500996
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600997 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500998 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600999 try:
1000 (providee, provider) = p.split(':')
1001 except:
1002 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
1003 continue
1004 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
1005 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
1006 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001007
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001008 def findConfigFilePath(self, configfile):
1009 """
1010 Find the location on disk of configfile and if it exists and was parsed by BitBake
1011 emit the ConfigFilePathFound event with the path to the file.
1012 """
1013 path = bb.cookerdata.findConfigFile(configfile, self.data)
1014 if not path:
1015 return
1016
1017 # Generate a list of parsed configuration files by searching the files
1018 # listed in the __depends and __base_depends variables with a .conf suffix.
1019 conffiles = []
1020 dep_files = self.data.getVar('__base_depends', False) or []
1021 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
1022
1023 for f in dep_files:
1024 if f[0].endswith(".conf"):
1025 conffiles.append(f[0])
1026
1027 _, conf, conffile = path.rpartition("conf/")
1028 match = os.path.join(conf, conffile)
1029 # Try and find matches for conf/conffilename.conf as we don't always
1030 # have the full path to the file.
1031 for cfg in conffiles:
1032 if cfg.endswith(match):
1033 bb.event.fire(bb.event.ConfigFilePathFound(path),
1034 self.data)
1035 break
1036
1037 def findFilesMatchingInDir(self, filepattern, directory):
1038 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001039 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001040 'directory' in each BBPATH. i.e. to find all rootfs package classes available
1041 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
1042 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001043 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001044 """
1045
1046 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001047 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001048 for path in bbpaths:
1049 dirpath = os.path.join(path, directory)
1050 if os.path.exists(dirpath):
1051 for root, dirs, files in os.walk(dirpath):
1052 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001053 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001054 matches.append(f)
1055
1056 if matches:
1057 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1058
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001059 def findProviders(self, mc=''):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001060 return bb.providers.findProviders(self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001061
1062 def findBestProvider(self, pn, mc=''):
1063 if pn in self.recipecaches[mc].providers:
1064 filenames = self.recipecaches[mc].providers[pn]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001065 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.databuilder.mcdata[mc], self.recipecaches[mc])
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001066 filename = eligible[0]
1067 return None, None, None, filename
1068 elif pn in self.recipecaches[mc].pkg_pn:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001069 return bb.providers.findBestProvider(pn, self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001070 else:
1071 return None, None, None, None
1072
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001073 def findConfigFiles(self, varname):
1074 """
1075 Find config files which are appropriate values for varname.
1076 i.e. MACHINE, DISTRO
1077 """
1078 possible = []
1079 var = varname.lower()
1080
1081 data = self.data
1082 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001083 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001084 for path in bbpaths:
1085 confpath = os.path.join(path, "conf", var)
1086 if os.path.exists(confpath):
1087 for root, dirs, files in os.walk(confpath):
1088 # get all child files, these are appropriate values
1089 for f in files:
1090 val, sep, end = f.rpartition('.')
1091 if end == 'conf':
1092 possible.append(val)
1093
1094 if possible:
1095 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1096
1097 def findInheritsClass(self, klass):
1098 """
1099 Find all recipes which inherit the specified class
1100 """
1101 pkg_list = []
1102
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001103 for pfn in self.recipecaches[''].pkg_fn:
1104 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001105 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001106 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001107
1108 return pkg_list
1109
1110 def generateTargetsTree(self, klass=None, pkgs=None):
1111 """
1112 Generate a dependency tree of buildable targets
1113 Generate an event with the result
1114 """
1115 # if the caller hasn't specified a pkgs list default to universe
1116 if not pkgs:
1117 pkgs = ['universe']
1118 # if inherited_class passed ensure all recipes which inherit the
1119 # specified class are included in pkgs
1120 if klass:
1121 extra_pkgs = self.findInheritsClass(klass)
1122 pkgs = pkgs + extra_pkgs
1123
1124 # generate a dependency tree for all our packages
1125 tree = self.generatePkgDepTreeData(pkgs, 'build')
1126 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1127
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001128 def interactiveMode( self ):
1129 """Drop off into a shell"""
1130 try:
1131 from bb import shell
1132 except ImportError:
1133 parselog.exception("Interactive mode not available")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001134 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001135 else:
1136 shell.start( self )
1137
1138
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001139 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001140 """Handle collections"""
1141 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001142 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001143 if collections:
1144 collection_priorities = {}
1145 collection_depends = {}
1146 collection_list = collections.split()
1147 min_prio = 0
1148 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001149 bb.debug(1,'Processing %s in collection list' % (c))
1150
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001151 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001152 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001153 if priority:
1154 try:
1155 prio = int(priority)
1156 except ValueError:
1157 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1158 errors = True
1159 if min_prio == 0 or prio < min_prio:
1160 min_prio = prio
1161 collection_priorities[c] = prio
1162 else:
1163 collection_priorities[c] = None
1164
1165 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001166 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001167 if deps:
1168 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001169 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001170 except bb.utils.VersionStringException as vse:
1171 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001172 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001173 if dep in collection_list:
1174 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001175 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001176 (op, depver) = opstr.split()
1177 if layerver:
1178 try:
1179 res = bb.utils.vercmp_string_op(layerver, depver, op)
1180 except bb.utils.VersionStringException as vse:
1181 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1182 if not res:
1183 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)
1184 errors = True
1185 else:
1186 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)
1187 errors = True
1188 else:
1189 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1190 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001191 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001192 else:
1193 collection_depends[c] = []
1194
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001195 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001196 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001197 if recs:
1198 try:
1199 recDict = bb.utils.explode_dep_versions2(recs)
1200 except bb.utils.VersionStringException as vse:
1201 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1202 for rec, oplist in list(recDict.items()):
1203 if rec in collection_list:
1204 if oplist:
1205 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001206 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001207 if layerver:
1208 (op, recver) = opstr.split()
1209 try:
1210 res = bb.utils.vercmp_string_op(layerver, recver, op)
1211 except bb.utils.VersionStringException as vse:
1212 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1213 if not res:
1214 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)
1215 continue
1216 else:
1217 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)
1218 continue
1219 parselog.debug(3,"Layer '%s' recommends layer '%s', so we are adding it", c, rec)
1220 collection_depends[c].append(rec)
1221 else:
1222 parselog.debug(3,"Layer '%s' recommends layer '%s', but this layer is not enabled in your configuration", c, rec)
1223
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001224 # Recursively work out collection priorities based on dependencies
1225 def calc_layer_priority(collection):
1226 if not collection_priorities[collection]:
1227 max_depprio = min_prio
1228 for dep in collection_depends[collection]:
1229 calc_layer_priority(dep)
1230 depprio = collection_priorities[dep]
1231 if depprio > max_depprio:
1232 max_depprio = depprio
1233 max_depprio += 1
1234 parselog.debug(1, "Calculated priority of layer %s as %d", collection, max_depprio)
1235 collection_priorities[collection] = max_depprio
1236
1237 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1238 for c in collection_list:
1239 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001240 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001241 if regex is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001242 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1243 errors = True
1244 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001245 elif regex == "":
1246 parselog.debug(1, "BBFILE_PATTERN_%s is empty" % c)
Brad Bishop19323692019-04-05 15:28:33 -04001247 cre = re.compile('^NULL$')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001248 errors = False
1249 else:
1250 try:
1251 cre = re.compile(regex)
1252 except re.error:
1253 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1254 errors = True
1255 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001256 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001257 if errors:
1258 # We've already printed the actual error(s)
1259 raise CollectionError("Errors during parsing layer configuration")
1260
1261 def buildSetVars(self):
1262 """
1263 Setup any variables needed before starting a build
1264 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001265 t = time.gmtime()
1266 for mc in self.databuilder.mcdata:
1267 ds = self.databuilder.mcdata[mc]
1268 if not ds.getVar("BUILDNAME", False):
1269 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1270 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1271 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1272 ds.setVar("TIME", time.strftime('%H%M%S', t))
1273
1274 def reset_mtime_caches(self):
1275 """
1276 Reset mtime caches - this is particularly important when memory resident as something
1277 which is cached is not unlikely to have changed since the last invocation (e.g. a
1278 file associated with a recipe might have been modified by the user).
1279 """
1280 build.reset_cache()
1281 bb.fetch._checksum_cache.mtime_cache.clear()
1282 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1283 if siggen_cache:
1284 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001285
Andrew Geissler5a43b432020-06-13 10:46:56 -05001286 def matchFiles(self, bf, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001287 """
1288 Find the .bb files which match the expression in 'buildfile'.
1289 """
1290 if bf.startswith("/") or bf.startswith("../"):
1291 bf = os.path.abspath(bf)
1292
Andrew Geissler5a43b432020-06-13 10:46:56 -05001293 self.collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)}
1294 filelist, masked, searchdirs = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001295 try:
1296 os.stat(bf)
1297 bf = os.path.abspath(bf)
1298 return [bf]
1299 except OSError:
1300 regexp = re.compile(bf)
1301 matches = []
1302 for f in filelist:
1303 if regexp.search(f) and os.path.isfile(f):
1304 matches.append(f)
1305 return matches
1306
Andrew Geissler5a43b432020-06-13 10:46:56 -05001307 def matchFile(self, buildfile, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001308 """
1309 Find the .bb file which matches the expression in 'buildfile'.
1310 Raise an error if multiple files
1311 """
Andrew Geissler5a43b432020-06-13 10:46:56 -05001312 matches = self.matchFiles(buildfile, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001313 if len(matches) != 1:
1314 if matches:
1315 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1316 if matches:
1317 for f in matches:
1318 msg += "\n %s" % f
1319 parselog.error(msg)
1320 else:
1321 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1322 raise NoSpecificMatch
1323 return matches[0]
1324
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001325 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001326 """
1327 Build the file matching regexp buildfile
1328 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001329 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001330
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001331 # Too many people use -b because they think it's how you normally
1332 # specify a target to be built, so show a warning
1333 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1334
1335 self.buildFileInternal(buildfile, task)
1336
1337 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1338 """
1339 Build the file matching regexp buildfile
1340 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001341
1342 # Parse the configuration here. We need to do it explicitly here since
1343 # buildFile() doesn't use the cache
1344 self.parseConfiguration()
1345
1346 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001347 if task is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001348 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001349 if not task.startswith("do_"):
1350 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001351
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001352 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001353 fn = self.matchFile(fn, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001354
1355 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001356 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001357
Andrew Geissler5a43b432020-06-13 10:46:56 -05001358 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001359
Andrew Geissler5a43b432020-06-13 10:46:56 -05001360 infos = bb_caches[mc].parse(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001361 infos = dict(infos)
1362
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001363 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001364 try:
1365 info_array = infos[fn]
1366 except KeyError:
1367 bb.fatal("%s does not exist" % fn)
1368
1369 if info_array[0].skipped:
1370 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1371
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001372 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001373
1374 # Tweak some variables
1375 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001376 self.recipecaches[mc].ignored_dependencies = set()
1377 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001378 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001379
1380 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001381 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1382 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001383 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1384 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001385
1386 # Invalidate task for target if force mode active
1387 if self.configuration.force:
1388 logger.verbose("Invalidate task %s, %s", task, fn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001389 bb.parse.siggen.invalidate_task(task, self.recipecaches[mc], fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001390
1391 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001392 taskdata = {}
1393 taskdata[mc] = bb.taskdata.TaskData(self.configuration.abort)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001394 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001395
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001396 if quietlog:
1397 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1398 bb.runqueue.logger.setLevel(logging.WARNING)
1399
1400 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1401 if fireevents:
1402 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001403
1404 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001405 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001406
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001407 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001408
1409 def buildFileIdle(server, rq, abort):
1410
1411 msg = None
1412 interrupted = 0
1413 if abort or self.state == state.forceshutdown:
1414 rq.finish_runqueue(True)
1415 msg = "Forced shutdown"
1416 interrupted = 2
1417 elif self.state == state.shutdown:
1418 rq.finish_runqueue(False)
1419 msg = "Stopped build"
1420 interrupted = 1
1421 failures = 0
1422 try:
1423 retval = rq.execute_runqueue()
1424 except runqueue.TaskFailure as exc:
1425 failures += len(exc.args)
1426 retval = False
1427 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001428 self.command.finishAsyncCommand(str(exc))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001429 if quietlog:
1430 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001431 return False
1432
1433 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001434 if fireevents:
1435 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 -05001436 self.command.finishAsyncCommand(msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001437 # We trashed self.recipecaches above
1438 self.parsecache_valid = False
1439 self.configuration.limited_deps = False
1440 bb.parse.siggen.reset(self.data)
1441 if quietlog:
1442 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001443 return False
1444 if retval is True:
1445 return True
1446 return retval
1447
Andrew Geissler635e0e42020-08-21 15:58:33 -05001448 self.idleCallBackRegister(buildFileIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001449
1450 def buildTargets(self, targets, task):
1451 """
1452 Attempt to build the targets specified
1453 """
1454
1455 def buildTargetsIdle(server, rq, abort):
1456 msg = None
1457 interrupted = 0
1458 if abort or self.state == state.forceshutdown:
1459 rq.finish_runqueue(True)
1460 msg = "Forced shutdown"
1461 interrupted = 2
1462 elif self.state == state.shutdown:
1463 rq.finish_runqueue(False)
1464 msg = "Stopped build"
1465 interrupted = 1
1466 failures = 0
1467 try:
1468 retval = rq.execute_runqueue()
1469 except runqueue.TaskFailure as exc:
1470 failures += len(exc.args)
1471 retval = False
1472 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001473 self.command.finishAsyncCommand(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001474 return False
1475
1476 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001477 try:
1478 for mc in self.multiconfigs:
1479 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1480 finally:
1481 self.command.finishAsyncCommand(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001482 return False
1483 if retval is True:
1484 return True
1485 return retval
1486
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001487 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001488 self.buildSetVars()
1489
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001490 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001491 if task is None:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001492 task = self.configuration.cmd
1493
1494 if not task.startswith("do_"):
1495 task = "do_%s" % task
1496
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001497 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1498
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001499 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001500
1501 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.abort)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001502
1503 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001504
1505 # make targets to always look as <target>:do_<task>
1506 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001507 for target in runlist:
1508 if target[0]:
Brad Bishop15ae2502019-06-18 21:44:24 -04001509 ntargets.append("mc:%s:%s:%s" % (target[0], target[1], target[2]))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001510 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001511
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001512 for mc in self.multiconfigs:
1513 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001514
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001515 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001516 if 'universe' in targets:
1517 rq.rqdata.warn_multi_bb = True
1518
Andrew Geissler635e0e42020-08-21 15:58:33 -05001519 self.idleCallBackRegister(buildTargetsIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001520
1521
1522 def getAllKeysWithFlags(self, flaglist):
1523 dump = {}
1524 for k in self.data.keys():
1525 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001526 expand = True
1527 flags = self.data.getVarFlags(k)
1528 if flags and "func" in flags and "python" in flags:
1529 expand = False
1530 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001531 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1532 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001533 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001534 'history' : self.data.varhistory.variable(k),
1535 }
1536 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001537 if flags and d in flags:
1538 dump[k][d] = flags[d]
1539 else:
1540 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001541 except Exception as e:
1542 print(e)
1543 return dump
1544
1545
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001546 def updateCacheSync(self):
1547 if self.state == state.running:
1548 return
1549
1550 # reload files for which we got notifications
1551 for p in self.inotify_modified_files:
1552 bb.parse.update_cache(p)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001553 if p in bb.parse.BBHandler.cached_statements:
1554 del bb.parse.BBHandler.cached_statements[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001555 self.inotify_modified_files = []
1556
1557 if not self.baseconfig_valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001558 logger.debug("Reloading base configuration data")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001559 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001560 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001561
1562 # This is called for all async commands when self.state != running
1563 def updateCache(self):
1564 if self.state == state.running:
1565 return
1566
1567 if self.state in (state.shutdown, state.forceshutdown, state.error):
1568 if hasattr(self.parser, 'shutdown'):
1569 self.parser.shutdown(clean=False, force = True)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001570 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001571 raise bb.BBHandledException()
1572
1573 if self.state != state.parsing:
1574 self.updateCacheSync()
1575
1576 if self.state != state.parsing and not self.parsecache_valid:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001577 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001578 self.parseConfiguration ()
1579 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001580 for mc in self.multiconfigs:
1581 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001582
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001583 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001584 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001585 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001586
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001587 for dep in self.configuration.extra_assume_provided:
1588 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001589
Andrew Geissler5a43b432020-06-13 10:46:56 -05001590 self.collections = {}
1591
1592 mcfilelist = {}
1593 total_masked = 0
1594 searchdirs = set()
1595 for mc in self.multiconfigs:
1596 self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc)
1597 (filelist, masked, search) = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
1598
1599 mcfilelist[mc] = filelist
1600 total_masked += masked
1601 searchdirs |= set(search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001602
1603 # Add inotify watches for directories searched for bb/bbappend files
1604 for dirent in searchdirs:
1605 self.add_filewatch([[dirent]], dirs=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001606
Andrew Geissler5a43b432020-06-13 10:46:56 -05001607 self.parser = CookerParser(self, mcfilelist, total_masked)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001608 self.parsecache_valid = True
1609
1610 self.state = state.parsing
1611
1612 if not self.parser.parse_next():
1613 collectlog.debug(1, "parsing complete")
1614 if self.parser.error:
1615 raise bb.BBHandledException()
1616 self.show_appends_with_no_recipes()
1617 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001618 for mc in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001619 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 -05001620 self.state = state.running
1621
1622 # Send an event listing all stamps reachable after parsing
1623 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001624 for mc in self.multiconfigs:
1625 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1626 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001627 return None
1628
1629 return True
1630
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001631 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001632
1633 # Return a copy, don't modify the original
1634 pkgs_to_build = pkgs_to_build[:]
1635
1636 if len(pkgs_to_build) == 0:
1637 raise NothingToBuild
1638
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001639 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001640 for pkg in pkgs_to_build.copy():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001641 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001642 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Brad Bishop15ae2502019-06-18 21:44:24 -04001643 if pkg.startswith("multiconfig:"):
1644 pkgs_to_build.remove(pkg)
1645 pkgs_to_build.append(pkg.replace("multiconfig:", "mc:"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001646
1647 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001648 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001649 for mc in self.multiconfigs:
1650 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1651 for t in self.recipecaches[mc].world_target:
1652 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001653 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001654 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001655
1656 if 'universe' in pkgs_to_build:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001657 parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001658 parselog.debug(1, "collating packages for \"universe\"")
1659 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001660 for mc in self.multiconfigs:
1661 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001662 if task:
1663 foundtask = False
1664 for provider_fn in self.recipecaches[mc].providers[t]:
1665 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1666 foundtask = True
1667 break
1668 if not foundtask:
1669 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1670 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001671 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001672 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001673 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001674
1675 return pkgs_to_build
1676
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001677 def pre_serve(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001678 return
1679
1680 def post_serve(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001681 self.shutdown(force=True)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001682 prserv.serv.auto_shutdown()
Brad Bishop08902b02019-08-20 09:16:51 -04001683 if self.hashserv:
1684 self.hashserv.process.terminate()
1685 self.hashserv.process.join()
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001686 if hasattr(self, "data"):
1687 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001688
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001689 def shutdown(self, force = False):
1690 if force:
1691 self.state = state.forceshutdown
1692 else:
1693 self.state = state.shutdown
1694
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001695 if self.parser:
1696 self.parser.shutdown(clean=not force, force=force)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001697 self.parser.final_cleanup()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001698
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001699 def finishcommand(self):
1700 self.state = state.initial
1701
1702 def reset(self):
1703 self.initConfigurationData()
Brad Bishop08902b02019-08-20 09:16:51 -04001704 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001705
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001706 def clientComplete(self):
1707 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001708 self.finishcommand()
1709 self.extraconfigdata = {}
1710 self.command.reset()
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001711 if hasattr(self, "data"):
1712 self.databuilder.reset()
1713 self.data = self.databuilder.data
Andrew Geissler82c905d2020-04-13 13:39:40 -05001714 self.parsecache_valid = False
1715 self.baseconfig_valid = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001716
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001717
1718class CookerExit(bb.event.Event):
1719 """
1720 Notify clients of the Cooker shutdown
1721 """
1722
1723 def __init__(self):
1724 bb.event.Event.__init__(self)
1725
1726
1727class CookerCollectFiles(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001728 def __init__(self, priorities, mc=''):
1729 self.mc = mc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001730 self.bbappends = []
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001731 # Priorities is a list of tupples, with the second element as the pattern.
1732 # We need to sort the list with the longest pattern first, and so on to
1733 # the shortest. This allows nested layers to be properly evaluated.
1734 self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001735
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001736 def calc_bbfile_priority(self, filename):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001737 for _, _, regex, pri in self.bbfile_config_priorities:
1738 if regex.match(filename):
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001739 return pri, regex
1740 return 0, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001741
1742 def get_bbfiles(self):
1743 """Get list of default .bb files by reading out the current directory"""
1744 path = os.getcwd()
1745 contents = os.listdir(path)
1746 bbfiles = []
1747 for f in contents:
1748 if f.endswith(".bb"):
1749 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1750 return bbfiles
1751
1752 def find_bbfiles(self, path):
1753 """Find all the .bb and .bbappend files in a directory"""
1754 found = []
1755 for dir, dirs, files in os.walk(path):
1756 for ignored in ('SCCS', 'CVS', '.svn'):
1757 if ignored in dirs:
1758 dirs.remove(ignored)
1759 found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
1760
1761 return found
1762
1763 def collect_bbfiles(self, config, eventdata):
1764 """Collect all available .bb build files"""
1765 masked = 0
1766
1767 collectlog.debug(1, "collecting .bb files")
1768
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001769 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001770
1771 # Sort files by priority
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001772 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem)[0] )
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001773 config.setVar("BBFILES_PRIORITIZED", " ".join(files))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001774
1775 if not len(files):
1776 files = self.get_bbfiles()
1777
1778 if not len(files):
1779 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1780 bb.event.fire(CookerExit(), eventdata)
1781
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001782 # We need to track where we look so that we can add inotify watches. There
1783 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001784 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001785 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001786 if hasattr(os, 'scandir'):
1787 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001788 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001789
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001790 def ourlistdir(d):
1791 searchdirs.append(d)
1792 return origlistdir(d)
1793
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001794 def ourscandir(d):
1795 searchdirs.append(d)
1796 return origscandir(d)
1797
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001798 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001799 if hasattr(os, 'scandir'):
1800 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001801 try:
1802 # Can't use set here as order is important
1803 newfiles = []
1804 for f in files:
1805 if os.path.isdir(f):
1806 dirfiles = self.find_bbfiles(f)
1807 for g in dirfiles:
1808 if g not in newfiles:
1809 newfiles.append(g)
1810 else:
1811 globbed = glob.glob(f)
1812 if not globbed and os.path.exists(f):
1813 globbed = [f]
1814 # glob gives files in order on disk. Sort to be deterministic.
1815 for g in sorted(globbed):
1816 if g not in newfiles:
1817 newfiles.append(g)
1818 finally:
1819 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001820 if hasattr(os, 'scandir'):
1821 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001822
1823 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001824
1825 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001826 # First validate the individual regular expressions and ignore any
1827 # that do not compile
1828 bbmasks = []
1829 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001830 # When constructing an older style single regex, it's possible for BBMASK
1831 # to end up beginning with '|', which matches and masks _everything_.
1832 if mask.startswith("|"):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001833 collectlog.warning("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001834 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001835 try:
1836 re.compile(mask)
1837 bbmasks.append(mask)
1838 except sre_constants.error:
1839 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1840
1841 # Then validate the combined regular expressions. This should never
1842 # fail, but better safe than sorry...
1843 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001844 try:
1845 bbmask_compiled = re.compile(bbmask)
1846 except sre_constants.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001847 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1848 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001849
1850 bbfiles = []
1851 bbappend = []
1852 for f in newfiles:
1853 if bbmask and bbmask_compiled.search(f):
1854 collectlog.debug(1, "skipping masked file %s", f)
1855 masked += 1
1856 continue
1857 if f.endswith('.bb'):
1858 bbfiles.append(f)
1859 elif f.endswith('.bbappend'):
1860 bbappend.append(f)
1861 else:
1862 collectlog.debug(1, "skipping %s: unknown file extension", f)
1863
1864 # Build a list of .bbappend files for each .bb file
1865 for f in bbappend:
1866 base = os.path.basename(f).replace('.bbappend', '.bb')
1867 self.bbappends.append((base, f))
1868
1869 # Find overlayed recipes
1870 # bbfiles will be in priority order which makes this easy
1871 bbfile_seen = dict()
1872 self.overlayed = defaultdict(list)
1873 for f in reversed(bbfiles):
1874 base = os.path.basename(f)
1875 if base not in bbfile_seen:
1876 bbfile_seen[base] = f
1877 else:
1878 topfile = bbfile_seen[base]
1879 self.overlayed[topfile].append(f)
1880
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001881 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001882
1883 def get_file_appends(self, fn):
1884 """
1885 Returns a list of .bbappend files to apply to fn
1886 """
1887 filelist = []
1888 f = os.path.basename(fn)
1889 for b in self.bbappends:
1890 (bbappend, filename) = b
1891 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1892 filelist.append(filename)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001893 return tuple(filelist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001894
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001895 def collection_priorities(self, pkgfns, fns, d):
1896 # Return the priorities of the entries in pkgfns
1897 # Also check that all the regexes in self.bbfile_config_priorities are used
1898 # (but to do that we need to ensure skipped recipes aren't counted, nor
1899 # collections in BBFILE_PATTERN_IGNORE_EMPTY)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001900
1901 priorities = {}
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001902 seen = set()
1903 matched = set()
1904
1905 matched_regex = set()
1906 unmatched_regex = set()
1907 for _, _, regex, _ in self.bbfile_config_priorities:
1908 unmatched_regex.add(regex)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001909
1910 # Calculate priorities for each file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001911 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001912 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001913 priorities[p], regex = self.calc_bbfile_priority(realfn)
1914 if regex in unmatched_regex:
1915 matched_regex.add(regex)
1916 unmatched_regex.remove(regex)
1917 seen.add(realfn)
1918 if regex:
1919 matched.add(realfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001920
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001921 if unmatched_regex:
1922 # Account for bbappend files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001923 for b in self.bbappends:
1924 (bbfile, append) = b
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001925 seen.add(append)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001926
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001927 # Account for skipped recipes
1928 seen.update(fns)
1929
1930 seen.difference_update(matched)
1931
1932 def already_matched(fn):
1933 for regex in matched_regex:
1934 if regex.match(fn):
1935 return True
1936 return False
1937
1938 for unmatch in unmatched_regex.copy():
1939 for fn in seen:
1940 if unmatch.match(fn):
1941 # If the bbappend or file was already matched by another regex, skip it
1942 # e.g. for a layer within a layer, the outer regex could match, the inner
1943 # regex may match nothing and we should warn about that
1944 if already_matched(fn):
1945 continue
1946 unmatched_regex.remove(unmatch)
1947 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001948
1949 for collection, pattern, regex, _ in self.bbfile_config_priorities:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001950 if regex in unmatched_regex:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001951 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Andrew Geissler5a43b432020-06-13 10:46:56 -05001952 collectlog.warning("No bb files in %s matched BBFILE_PATTERN_%s '%s'" % (self.mc if self.mc else 'default',
1953 collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001954
1955 return priorities
1956
1957class ParsingFailure(Exception):
1958 def __init__(self, realexception, recipe):
1959 self.realexception = realexception
1960 self.recipe = recipe
1961 Exception.__init__(self, realexception, recipe)
1962
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001963class Parser(multiprocessing.Process):
1964 def __init__(self, jobs, results, quit, init, profile):
1965 self.jobs = jobs
1966 self.results = results
1967 self.quit = quit
1968 self.init = init
1969 multiprocessing.Process.__init__(self)
1970 self.context = bb.utils.get_context().copy()
1971 self.handlers = bb.event.get_class_handlers().copy()
1972 self.profile = profile
1973
1974 def run(self):
1975
1976 if not self.profile:
1977 self.realrun()
1978 return
1979
1980 try:
1981 import cProfile as profile
1982 except:
1983 import profile
1984 prof = profile.Profile()
1985 try:
1986 profile.Profile.runcall(prof, self.realrun)
1987 finally:
1988 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
1989 prof.dump_stats(logfile)
1990
1991 def realrun(self):
1992 if self.init:
1993 self.init()
1994
1995 pending = []
1996 while True:
1997 try:
1998 self.quit.get_nowait()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001999 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002000 pass
2001 else:
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002002 self.results.close()
2003 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002004 break
2005
2006 if pending:
2007 result = pending.pop()
2008 else:
2009 try:
Brad Bishop19323692019-04-05 15:28:33 -04002010 job = self.jobs.pop()
2011 except IndexError:
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002012 self.results.close()
2013 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002014 break
2015 result = self.parse(*job)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002016 # Clear the siggen cache after parsing to control memory usage, its huge
2017 bb.parse.siggen.postparsing_clean_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002018 try:
2019 self.results.put(result, timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002020 except queue.Full:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002021 pending.append(result)
2022
Andrew Geissler5a43b432020-06-13 10:46:56 -05002023 def parse(self, mc, cache, filename, appends):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002024 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002025 origfilter = bb.event.LogHandler.filter
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002026 # Record the filename we're parsing into any events generated
2027 def parse_filter(self, record):
2028 record.taskpid = bb.event.worker_pid
2029 record.fn = filename
2030 return True
2031
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002032 # Reset our environment and handlers to the original settings
2033 bb.utils.set_context(self.context.copy())
2034 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002035 bb.event.LogHandler.filter = parse_filter
2036
Andrew Geissler5a43b432020-06-13 10:46:56 -05002037 return True, mc, cache.parse(filename, appends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002038 except Exception as exc:
2039 tb = sys.exc_info()[2]
2040 exc.recipe = filename
2041 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
2042 return True, exc
2043 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
2044 # and for example a worker thread doesn't just exit on its own in response to
2045 # a SystemExit event for example.
2046 except BaseException as exc:
2047 return True, ParsingFailure(exc, filename)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002048 finally:
2049 bb.event.LogHandler.filter = origfilter
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002050
2051class CookerParser(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002052 def __init__(self, cooker, mcfilelist, masked):
2053 self.mcfilelist = mcfilelist
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002054 self.cooker = cooker
2055 self.cfgdata = cooker.data
2056 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002057 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002058
2059 # Accounting statistics
2060 self.parsed = 0
2061 self.cached = 0
2062 self.error = 0
2063 self.masked = masked
2064
2065 self.skipped = 0
2066 self.virtuals = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002067
2068 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002069 self.process_names = []
2070
Andrew Geissler5a43b432020-06-13 10:46:56 -05002071 self.bb_caches = bb.cache.MulticonfigCache(self.cfgbuilder, self.cfghash, cooker.caches_array)
2072 self.fromcache = set()
2073 self.willparse = set()
2074 for mc in self.cooker.multiconfigs:
2075 for filename in self.mcfilelist[mc]:
2076 appends = self.cooker.collections[mc].get_file_appends(filename)
2077 if not self.bb_caches[mc].cacheValid(filename, appends):
2078 self.willparse.add((mc, self.bb_caches[mc], filename, appends))
2079 else:
2080 self.fromcache.add((mc, self.bb_caches[mc], filename, appends))
2081
2082 self.total = len(self.fromcache) + len(self.willparse)
2083 self.toparse = len(self.willparse)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002084 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002085
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002086 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Andrew Geissler5a43b432020-06-13 10:46:56 -05002087 multiprocessing.cpu_count()), self.toparse)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002088
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002089 self.start()
2090 self.haveshutdown = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002091 self.syncthread = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002092
2093 def start(self):
2094 self.results = self.load_cached()
2095 self.processes = []
2096 if self.toparse:
2097 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
2098 def init():
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002099 signal.signal(signal.SIGTERM, signal.SIG_DFL)
2100 signal.signal(signal.SIGHUP, signal.SIG_DFL)
2101 signal.signal(signal.SIGINT, signal.SIG_IGN)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002102 bb.utils.set_process_name(multiprocessing.current_process().name)
2103 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2104 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002105
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002106 self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002107 self.result_queue = multiprocessing.Queue()
Brad Bishop19323692019-04-05 15:28:33 -04002108
2109 def chunkify(lst,n):
2110 return [lst[i::n] for i in range(n)]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002111 self.jobs = chunkify(list(self.willparse), self.num_processes)
Brad Bishop19323692019-04-05 15:28:33 -04002112
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002113 for i in range(0, self.num_processes):
Brad Bishop19323692019-04-05 15:28:33 -04002114 parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, init, self.cooker.configuration.profile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002115 parser.start()
2116 self.process_names.append(parser.name)
2117 self.processes.append(parser)
2118
2119 self.results = itertools.chain(self.results, self.parse_generator())
2120
2121 def shutdown(self, clean=True, force=False):
2122 if not self.toparse:
2123 return
2124 if self.haveshutdown:
2125 return
2126 self.haveshutdown = True
2127
2128 if clean:
2129 event = bb.event.ParseCompleted(self.cached, self.parsed,
2130 self.skipped, self.masked,
2131 self.virtuals, self.error,
2132 self.total)
2133
2134 bb.event.fire(event, self.cfgdata)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002135
2136 for process in self.processes:
2137 self.parser_quit.put(None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002138
Brad Bishop08902b02019-08-20 09:16:51 -04002139 # Cleanup the queue before call process.join(), otherwise there might be
2140 # deadlocks.
2141 while True:
2142 try:
2143 self.result_queue.get(timeout=0.25)
2144 except queue.Empty:
2145 break
2146
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002147 for process in self.processes:
2148 if force:
2149 process.join(.1)
2150 process.terminate()
2151 else:
2152 process.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002153
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002154 self.parser_quit.close()
2155 # Allow data left in the cancel queue to be discarded
2156 self.parser_quit.cancel_join_thread()
2157
Andrew Geissler5a43b432020-06-13 10:46:56 -05002158 def sync_caches():
2159 for c in self.bb_caches.values():
2160 c.sync()
2161
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002162 sync = threading.Thread(target=sync_caches, name="SyncThread")
2163 self.syncthread = sync
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002164 sync.start()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002165 bb.codeparser.parser_cache_savemerge()
2166 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002167 if self.cooker.configuration.profile:
2168 profiles = []
2169 for i in self.process_names:
2170 logfile = "profile-parse-%s.log" % i
2171 if os.path.exists(logfile):
2172 profiles.append(logfile)
2173
2174 pout = "profile-parse.log.processed"
2175 bb.utils.process_profilelog(profiles, pout = pout)
2176 print("Processed parsing statistics saved to %s" % (pout))
2177
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002178 def final_cleanup(self):
2179 if self.syncthread:
2180 self.syncthread.join()
2181
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002182 def load_cached(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002183 for mc, cache, filename, appends in self.fromcache:
2184 cached, infos = cache.load(filename, appends)
2185 yield not cached, mc, infos
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002186
2187 def parse_generator(self):
2188 while True:
2189 if self.parsed >= self.toparse:
2190 break
2191
2192 try:
2193 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002194 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002195 pass
2196 else:
2197 value = result[1]
2198 if isinstance(value, BaseException):
2199 raise value
2200 else:
2201 yield result
2202
2203 def parse_next(self):
2204 result = []
2205 parsed = None
2206 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002207 parsed, mc, result = next(self.results)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002208 except StopIteration:
2209 self.shutdown()
2210 return False
2211 except bb.BBHandledException as exc:
2212 self.error += 1
2213 logger.error('Failed to parse recipe: %s' % exc.recipe)
2214 self.shutdown(clean=False)
2215 return False
2216 except ParsingFailure as exc:
2217 self.error += 1
2218 logger.error('Unable to parse %s: %s' %
2219 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
2220 self.shutdown(clean=False)
2221 return False
2222 except bb.parse.ParseError as exc:
2223 self.error += 1
2224 logger.error(str(exc))
2225 self.shutdown(clean=False)
2226 return False
2227 except bb.data_smart.ExpansionError as exc:
2228 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002229 bbdir = os.path.dirname(__file__) + os.sep
2230 etype, value, _ = sys.exc_info()
2231 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2232 logger.error('ExpansionError during parsing %s', value.recipe,
2233 exc_info=(etype, value, tb))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002234 self.shutdown(clean=False)
2235 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002236 except Exception as exc:
2237 self.error += 1
2238 etype, value, tb = sys.exc_info()
2239 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002240 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002241 exc_info=(etype, value, exc.traceback))
2242 else:
2243 # Most likely, an exception occurred during raising an exception
2244 import traceback
2245 logger.error('Exception during parse: %s' % traceback.format_exc())
2246 self.shutdown(clean=False)
2247 return False
2248
2249 self.current += 1
2250 self.virtuals += len(result)
2251 if parsed:
2252 self.parsed += 1
2253 if self.parsed % self.progress_chunk == 0:
2254 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2255 self.cfgdata)
2256 else:
2257 self.cached += 1
2258
2259 for virtualfn, info_array in result:
2260 if info_array[0].skipped:
2261 self.skipped += 1
2262 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Andrew Geissler5a43b432020-06-13 10:46:56 -05002263 self.bb_caches[mc].add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002264 parsed=parsed, watcher = self.cooker.add_filewatch)
2265 return True
2266
2267 def reparse(self, filename):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002268 to_reparse = set()
2269 for mc in self.cooker.multiconfigs:
2270 to_reparse.add((mc, filename, self.cooker.collections[mc].get_file_appends(filename)))
2271
2272 for mc, filename, appends in to_reparse:
2273 infos = self.bb_caches[mc].parse(filename, appends)
2274 for vfn, info_array in infos:
2275 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)