blob: 1f55d9ad73ec481f901d8d7986e070dc223248b3 [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:
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500385 bb.fatal("Unable to start PR Server, exiting, check the bitbake-cookerdaemon.log")
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")
Andrew Geissler5199d832021-09-24 16:47:35 -0500392 self.hashserv = hashserv.create_server(
393 self.hashservaddr,
394 dbfile,
395 sync=False,
396 upstream=self.data.getVar("BB_HASHSERVE_UPSTREAM") or None,
397 )
Patrick Williams213cb262021-08-07 19:21:33 -0500398 self.hashserv.serve_as_process()
Brad Bishopa34c0302019-09-23 22:34:48 -0400399 self.data.setVar("BB_HASHSERVE", self.hashservaddr)
400 self.databuilder.origdata.setVar("BB_HASHSERVE", self.hashservaddr)
401 self.databuilder.data.setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400402 for mc in self.databuilder.mcdata:
Brad Bishopa34c0302019-09-23 22:34:48 -0400403 self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400404
405 bb.parse.init_parser(self.data)
406
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500407 def enableDataTracking(self):
408 self.configuration.tracking = True
409 if hasattr(self, "data"):
410 self.data.enableTracking()
411
412 def disableDataTracking(self):
413 self.configuration.tracking = False
414 if hasattr(self, "data"):
415 self.data.disableTracking()
416
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500417 def parseConfiguration(self):
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600418 self.updateCacheSync()
419
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500421 nice = self.data.getVar("BB_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500422 if nice:
423 curnice = os.nice(0)
424 nice = int(nice) - curnice
425 buildlog.verbose("Renice to %s " % os.nice(nice))
426
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600427 if self.recipecaches:
428 del self.recipecaches
429 self.multiconfigs = self.databuilder.mcdata.keys()
430 self.recipecaches = {}
431 for mc in self.multiconfigs:
432 self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500433
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500434 self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500435
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500436 self.parsecache_valid = False
437
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500438 def updateConfigOpts(self, options, environment, cmdline):
439 self.ui_cmdline = cmdline
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500440 clean = True
441 for o in options:
442 if o in ['prefile', 'postfile']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500443 # Only these options may require a reparse
444 try:
445 if getattr(self.configuration, o) == options[o]:
446 # Value is the same, no need to mark dirty
447 continue
448 except AttributeError:
449 pass
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600450 logger.debug("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500451 print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500452 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500453 if hasattr(self.configuration, o):
454 setattr(self.configuration, o, options[o])
455
456 if self.configuration.writeeventlog:
457 if self.eventlog and self.eventlog[0] != self.configuration.writeeventlog:
458 bb.event.unregister_UIHhandler(self.eventlog[1])
459 if not self.eventlog or self.eventlog[0] != self.configuration.writeeventlog:
460 # we log all events to a file if so directed
461 # register the log file writer as UI Handler
462 writer = EventWriter(self, self.configuration.writeeventlog)
463 EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
464 self.eventlog = (self.configuration.writeeventlog, bb.event.register_UIHhandler(EventLogWriteHandler(writer)))
465
466 bb.msg.loggerDefaultLogLevel = self.configuration.default_loglevel
467 bb.msg.loggerDefaultDomains = self.configuration.debug_domains
468
469 if hasattr(self, "data"):
470 origenv = bb.data.init()
471 for k in environment:
472 origenv.setVar(k, environment[k])
473 self.data.setVar("BB_ORIGENV", origenv)
474
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500475 for k in bb.utils.approved_variables():
476 if k in environment and k not in self.configuration.env:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600477 logger.debug("Updating new environment variable %s to %s" % (k, environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500478 self.configuration.env[k] = environment[k]
479 clean = False
480 if k in self.configuration.env and k not in environment:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600481 logger.debug("Updating environment variable %s (deleted)" % (k))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500482 del self.configuration.env[k]
483 clean = False
484 if k not in self.configuration.env and k not in environment:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500485 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500486 if environment[k] != self.configuration.env[k]:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600487 logger.debug("Updating environment variable %s from %s to %s" % (k, self.configuration.env[k], environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488 self.configuration.env[k] = environment[k]
489 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500490
491 # Now update all the variables not in the datastore to match
492 self.configuration.env = environment
493
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500494 if not clean:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600495 logger.debug("Base environment change, triggering reparse")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500496 self.reset()
497
498 def runCommands(self, server, data, abort):
499 """
500 Run any queued asynchronous command
501 This is done by the idle handler so it runs in true context rather than
502 tied to any UI.
503 """
504
505 return self.command.runAsyncCommand()
506
507 def showVersions(self):
508
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500509 (latest_versions, preferred_versions, required) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500510
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500511 logger.plain("%-35s %25s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version", "Required Version")
512 logger.plain("%-35s %25s %25s %25s\n", "===========", "==============", "=================", "================")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500513
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500514 for p in sorted(self.recipecaches[''].pkg_pn):
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500515 preferred = preferred_versions[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500516 latest = latest_versions[p]
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500517 requiredstr = ""
518 preferredstr = ""
519 if required[p]:
520 if preferred[0] is not None:
521 requiredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
522 else:
523 bb.fatal("REQUIRED_VERSION of package %s not available" % p)
524 else:
525 preferredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500526
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500527 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
528
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500529 if preferred == latest:
530 preferredstr = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500531
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500532 logger.plain("%-35s %25s %25s %25s", p, lateststr, preferredstr, requiredstr)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500533
534 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
535 """
536 Show the outer or per-recipe environment
537 """
538 fn = None
539 envdata = None
Brad Bishop15ae2502019-06-18 21:44:24 -0400540 mc = ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500541 if not pkgs_to_build:
542 pkgs_to_build = []
543
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500544 orig_tracking = self.configuration.tracking
545 if not orig_tracking:
546 self.enableDataTracking()
547 self.reset()
548
Brad Bishop15ae2502019-06-18 21:44:24 -0400549 def mc_base(p):
550 if p.startswith('mc:'):
551 s = p.split(':')
552 if len(s) == 2:
553 return s[1]
554 return None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500555
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500556 if buildfile:
557 # Parse the configuration here. We need to do it explicitly here since
558 # this showEnvironment() code path doesn't use the cache
559 self.parseConfiguration()
560
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600561 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500562 fn = self.matchFile(fn, mc)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600563 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500564 elif len(pkgs_to_build) == 1:
Brad Bishop15ae2502019-06-18 21:44:24 -0400565 mc = mc_base(pkgs_to_build[0])
566 if not mc:
567 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
568 if pkgs_to_build[0] in set(ignore.split()):
569 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500570
Brad Bishop15ae2502019-06-18 21:44:24 -0400571 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.abort, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500572
Brad Bishop15ae2502019-06-18 21:44:24 -0400573 mc = runlist[0][0]
574 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500575
576 if fn:
577 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -0500578 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
579 envdata = bb_caches[mc].loadDataFull(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500580 except Exception as e:
581 parselog.exception("Unable to read %s", fn)
582 raise
Brad Bishop15ae2502019-06-18 21:44:24 -0400583 else:
584 if not mc in self.databuilder.mcdata:
585 bb.fatal('Not multiconfig named "%s" found' % mc)
586 envdata = self.databuilder.mcdata[mc]
587 data.expandKeys(envdata)
588 parse.ast.runAnonFuncs(envdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500589
590 # Display history
591 with closing(StringIO()) as env:
592 self.data.inchistory.emit(env)
593 logger.plain(env.getvalue())
594
595 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500596 with closing(StringIO()) as env:
597 data.emit_env(env, envdata, True)
598 logger.plain(env.getvalue())
599
600 # emit the metadata which isnt valid shell
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500601 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600602 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500603 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500604
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500605 if not orig_tracking:
606 self.disableDataTracking()
607 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500608
609 def buildTaskData(self, pkgs_to_build, task, abort, allowincomplete=False):
610 """
611 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
612 """
613 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
614
615 # A task of None means use the default task
616 if task is None:
617 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500618 if not task.startswith("do_"):
619 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500620
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500621 targetlist = self.checkPackages(pkgs_to_build, task)
622 fulltargetlist = []
623 defaulttask_implicit = ''
624 defaulttask_explicit = False
625 wildcard = False
626
627 # Wild card expansion:
Brad Bishop15ae2502019-06-18 21:44:24 -0400628 # Replace string such as "mc:*:bash"
629 # into "mc:A:bash mc:B:bash bash"
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500630 for k in targetlist:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600631 if k.startswith("mc:") and k.count(':') >= 2:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500632 if wildcard:
633 bb.fatal('multiconfig conflict')
634 if k.split(":")[1] == "*":
635 wildcard = True
636 for mc in self.multiconfigs:
637 if mc:
638 fulltargetlist.append(k.replace('*', mc))
639 # implicit default task
640 else:
641 defaulttask_implicit = k.split(":")[2]
642 else:
643 fulltargetlist.append(k)
644 else:
645 defaulttask_explicit = True
646 fulltargetlist.append(k)
647
648 if not defaulttask_explicit and defaulttask_implicit != '':
649 fulltargetlist.append(defaulttask_implicit)
650
651 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600652 taskdata = {}
653 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600655 for mc in self.multiconfigs:
656 taskdata[mc] = bb.taskdata.TaskData(abort, skiplist=self.skiplist, allowincomplete=allowincomplete)
657 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600658 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500659
660 current = 0
661 runlist = []
662 for k in fulltargetlist:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500663 origk = k
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600664 mc = ""
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600665 if k.startswith("mc:") and k.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600666 mc = k.split(":")[1]
667 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500668 ktask = task
669 if ":do_" in k:
670 k2 = k.split(":do_")
671 k = k2[0]
672 ktask = k2[1]
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500673
674 if mc not in self.multiconfigs:
675 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named %s" % (origk, mc))
676
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600677 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500678 current += 1
679 if not ktask.startswith("do_"):
680 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600681 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
682 # e.g. in ASSUME_PROVIDED
683 continue
684 fn = taskdata[mc].build_targets[k][0]
685 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500686 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600687
Brad Bishop15ae2502019-06-18 21:44:24 -0400688 havemc = False
689 for mc in self.multiconfigs:
690 if taskdata[mc].get_mcdepends():
691 havemc = True
Brad Bishopf058f492019-01-28 23:50:33 -0500692
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800693 # No need to do check providers if there are no mcdeps or not an mc build
Brad Bishop15ae2502019-06-18 21:44:24 -0400694 if havemc or len(self.multiconfigs) > 1:
Andrew Geissler99467da2019-02-25 18:54:23 -0600695 seen = set()
696 new = True
697 # Make sure we can provide the multiconfig dependency
698 while new:
699 mcdeps = set()
700 # Add unresolved first, so we can get multiconfig indirect dependencies on time
701 for mc in self.multiconfigs:
702 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
703 mcdeps |= set(taskdata[mc].get_mcdepends())
704 new = False
705 for mc in self.multiconfigs:
706 for k in mcdeps:
707 if k in seen:
708 continue
709 l = k.split(':')
710 depmc = l[2]
711 if depmc not in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500712 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named configuration %s" % (k,depmc))
Andrew Geissler99467da2019-02-25 18:54:23 -0600713 else:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600714 logger.debug("Adding providers for multiconfig dependency %s" % l[3])
Andrew Geissler99467da2019-02-25 18:54:23 -0600715 taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
716 seen.add(k)
717 new = True
Brad Bishopf058f492019-01-28 23:50:33 -0500718
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600719 for mc in self.multiconfigs:
720 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
721
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500722 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500724
725 def prepareTreeData(self, pkgs_to_build, task):
726 """
727 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
728 """
729
730 # We set abort to False here to prevent unbuildable targets raising
731 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600732 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500733
734 return runlist, taskdata
735
736 ######## WARNING : this function requires cache_extra to be enabled ########
737
738 def generateTaskDepTreeData(self, pkgs_to_build, task):
739 """
740 Create a dependency graph of pkgs_to_build including reverse dependency
741 information.
742 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500743 if not task.startswith("do_"):
744 task = "do_%s" % task
745
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600747 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500748 rq.rqdata.prepare()
749 return self.buildDependTree(rq, taskdata)
750
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600751 @staticmethod
752 def add_mc_prefix(mc, pn):
753 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -0400754 return "mc:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600755 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756
757 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600758 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759 depend_tree = {}
760 depend_tree["depends"] = {}
761 depend_tree["tdepends"] = {}
762 depend_tree["pn"] = {}
763 depend_tree["rdepends-pn"] = {}
764 depend_tree["packages"] = {}
765 depend_tree["rdepends-pkg"] = {}
766 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500767 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600768 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500769
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600770 for mc in taskdata:
771 for name, fn in list(taskdata[mc].get_providermap().items()):
772 pn = self.recipecaches[mc].pkg_fn[fn]
773 pn = self.add_mc_prefix(mc, pn)
774 if name != pn:
775 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
776 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500777
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600778 for tid in rq.rqdata.runtaskentries:
779 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
780 pn = self.recipecaches[mc].pkg_fn[taskfn]
781 pn = self.add_mc_prefix(mc, pn)
782 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500783 if pn not in depend_tree["pn"]:
784 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600785 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500786 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600787 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500788
789 # if we have extra caches, list all attributes they bring in
790 extra_info = []
791 for cache_class in self.caches_array:
792 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
793 cachefields = getattr(cache_class, 'cachefields', [])
794 extra_info = extra_info + cachefields
795
796 # for all attributes stored, add them to the dependency tree
797 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500799
800
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500801 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
802 if not dotname in depend_tree["tdepends"]:
803 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600804 for dep in rq.rqdata.runtaskentries[tid].depends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800805 (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
806 deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600807 depend_tree["tdepends"][dotname].append("%s.%s" % (deppn, bb.runqueue.taskname_from_tid(dep)))
808 if taskfn not in seen_fns:
809 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500810 packages = []
811
812 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600813 for dep in taskdata[mc].depids[taskfn]:
814 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500815
816 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600817 for rdep in taskdata[mc].rdepids[taskfn]:
818 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500819
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600820 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821 for package in rdepends:
822 depend_tree["rdepends-pkg"][package] = []
823 for rdepend in rdepends[package]:
824 depend_tree["rdepends-pkg"][package].append(rdepend)
825 packages.append(package)
826
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600827 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500828 for package in rrecs:
829 depend_tree["rrecs-pkg"][package] = []
830 for rdepend in rrecs[package]:
831 depend_tree["rrecs-pkg"][package].append(rdepend)
832 if not package in packages:
833 packages.append(package)
834
835 for package in packages:
836 if package not in depend_tree["packages"]:
837 depend_tree["packages"][package] = {}
838 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600839 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500840 depend_tree["packages"][package]["version"] = version
841
842 return depend_tree
843
844 ######## WARNING : this function requires cache_extra to be enabled ########
845 def generatePkgDepTreeData(self, pkgs_to_build, task):
846 """
847 Create a dependency tree of pkgs_to_build, returning the data.
848 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500849 if not task.startswith("do_"):
850 task = "do_%s" % task
851
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500852 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500853
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600854 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500855 depend_tree = {}
856 depend_tree["depends"] = {}
857 depend_tree["pn"] = {}
858 depend_tree["rdepends-pn"] = {}
859 depend_tree["rdepends-pkg"] = {}
860 depend_tree["rrecs-pkg"] = {}
861
862 # if we have extra caches, list all attributes they bring in
863 extra_info = []
864 for cache_class in self.caches_array:
865 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
866 cachefields = getattr(cache_class, 'cachefields', [])
867 extra_info = extra_info + cachefields
868
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600869 tids = []
870 for mc in taskdata:
871 for tid in taskdata[mc].taskentries:
872 tids.append(tid)
873
874 for tid in tids:
875 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
876
877 pn = self.recipecaches[mc].pkg_fn[taskfn]
878 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500879
880 if pn not in depend_tree["pn"]:
881 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600882 depend_tree["pn"][pn]["filename"] = taskfn
883 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500884 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600885 rdepends = self.recipecaches[mc].rundeps[taskfn]
886 rrecs = self.recipecaches[mc].runrecs[taskfn]
887 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500888
889 # for all extra attributes stored, add them to the dependency tree
890 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600891 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500892
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600893 if taskfn not in seen_fns:
894 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500895
896 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500897 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500898 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600899 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
900 fn_provider = taskdata[mc].build_targets[dep][0]
901 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500902 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500903 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600904 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500905 depend_tree["depends"][pn].append(pn_provider)
906
907 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600908 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500909 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600910 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
911 fn_rprovider = taskdata[mc].run_targets[rdep][0]
912 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500913 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600914 pn_rprovider = rdep
915 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500916 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
917
918 depend_tree["rdepends-pkg"].update(rdepends)
919 depend_tree["rrecs-pkg"].update(rrecs)
920
921 return depend_tree
922
923 def generateDepTreeEvent(self, pkgs_to_build, task):
924 """
925 Create a task dependency graph of pkgs_to_build.
926 Generate an event with the result
927 """
928 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
929 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
930
931 def generateDotGraphFiles(self, pkgs_to_build, task):
932 """
933 Create a task dependency graph of pkgs_to_build.
934 Save the result to a set of .dot files.
935 """
936
937 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
938
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500939 with open('pn-buildlist', 'w') as f:
940 for pn in depgraph["pn"]:
941 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500942 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500943
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500944 # Remove old format output files to ensure no confusion with stale data
945 try:
946 os.unlink('pn-depends.dot')
947 except FileNotFoundError:
948 pass
949 try:
950 os.unlink('package-depends.dot')
951 except FileNotFoundError:
952 pass
Brad Bishop79641f22019-09-10 07:20:22 -0400953 try:
954 os.unlink('recipe-depends.dot')
955 except FileNotFoundError:
956 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500957
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500958 with open('task-depends.dot', 'w') as f:
959 f.write("digraph depends {\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400960 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500961 (pn, taskname) = task.rsplit(".", 1)
962 fn = depgraph["pn"][pn]["filename"]
963 version = depgraph["pn"][pn]["version"]
964 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 -0400965 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500966 f.write('"%s" -> "%s"\n' % (task, dep))
967 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500968 logger.info("Task dependencies saved to 'task-depends.dot'")
969
970 def show_appends_with_no_recipes(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500971 appends_without_recipes = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500972 # Determine which bbappends haven't been applied
Andrew Geissler5a43b432020-06-13 10:46:56 -0500973 for mc in self.multiconfigs:
974 # First get list of recipes, including skipped
975 recipefns = list(self.recipecaches[mc].pkg_fn.keys())
976 recipefns.extend(self.skiplist.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500977
Andrew Geissler5a43b432020-06-13 10:46:56 -0500978 # Work out list of bbappends that have been applied
979 applied_appends = []
980 for fn in recipefns:
981 applied_appends.extend(self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500982
Andrew Geissler5a43b432020-06-13 10:46:56 -0500983 appends_without_recipes[mc] = []
984 for _, appendfn in self.collections[mc].bbappends:
985 if not appendfn in applied_appends:
986 appends_without_recipes[mc].append(appendfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500987
Andrew Geissler5a43b432020-06-13 10:46:56 -0500988 msgs = []
989 for mc in sorted(appends_without_recipes.keys()):
990 if appends_without_recipes[mc]:
991 msgs.append('No recipes in %s available for:\n %s' % (mc if mc else 'default',
992 '\n '.join(appends_without_recipes[mc])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500993
Andrew Geissler5a43b432020-06-13 10:46:56 -0500994 if msgs:
995 msg = "\n".join(msgs)
996 warn_only = self.databuilder.mcdata[mc].getVar("BB_DANGLINGAPPENDS_WARNONLY", \
997 False) or "no"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500998 if warn_only.lower() in ("1", "yes", "true"):
999 bb.warn(msg)
1000 else:
1001 bb.fatal(msg)
1002
1003 def handlePrefProviders(self):
1004
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001005 for mc in self.multiconfigs:
1006 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001007 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001008
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001009 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001010 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001011 try:
1012 (providee, provider) = p.split(':')
1013 except:
1014 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
1015 continue
1016 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
1017 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
1018 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001019
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001020 def findConfigFilePath(self, configfile):
1021 """
1022 Find the location on disk of configfile and if it exists and was parsed by BitBake
1023 emit the ConfigFilePathFound event with the path to the file.
1024 """
1025 path = bb.cookerdata.findConfigFile(configfile, self.data)
1026 if not path:
1027 return
1028
1029 # Generate a list of parsed configuration files by searching the files
1030 # listed in the __depends and __base_depends variables with a .conf suffix.
1031 conffiles = []
1032 dep_files = self.data.getVar('__base_depends', False) or []
1033 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
1034
1035 for f in dep_files:
1036 if f[0].endswith(".conf"):
1037 conffiles.append(f[0])
1038
1039 _, conf, conffile = path.rpartition("conf/")
1040 match = os.path.join(conf, conffile)
1041 # Try and find matches for conf/conffilename.conf as we don't always
1042 # have the full path to the file.
1043 for cfg in conffiles:
1044 if cfg.endswith(match):
1045 bb.event.fire(bb.event.ConfigFilePathFound(path),
1046 self.data)
1047 break
1048
1049 def findFilesMatchingInDir(self, filepattern, directory):
1050 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001051 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001052 'directory' in each BBPATH. i.e. to find all rootfs package classes available
1053 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
1054 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001055 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001056 """
1057
1058 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001059 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001060 for path in bbpaths:
1061 dirpath = os.path.join(path, directory)
1062 if os.path.exists(dirpath):
1063 for root, dirs, files in os.walk(dirpath):
1064 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001065 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001066 matches.append(f)
1067
1068 if matches:
1069 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1070
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001071 def findProviders(self, mc=''):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001072 return bb.providers.findProviders(self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001073
1074 def findBestProvider(self, pn, mc=''):
1075 if pn in self.recipecaches[mc].providers:
1076 filenames = self.recipecaches[mc].providers[pn]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001077 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.databuilder.mcdata[mc], self.recipecaches[mc])
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001078 if eligible is not None:
1079 filename = eligible[0]
1080 else:
1081 filename = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001082 return None, None, None, filename
1083 elif pn in self.recipecaches[mc].pkg_pn:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001084 (latest, latest_f, preferred_ver, preferred_file, required) = bb.providers.findBestProvider(pn, self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
1085 if required and preferred_file is None:
1086 return None, None, None, None
1087 return (latest, latest_f, preferred_ver, preferred_file)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001088 else:
1089 return None, None, None, None
1090
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001091 def findConfigFiles(self, varname):
1092 """
1093 Find config files which are appropriate values for varname.
1094 i.e. MACHINE, DISTRO
1095 """
1096 possible = []
1097 var = varname.lower()
1098
1099 data = self.data
1100 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001101 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001102 for path in bbpaths:
1103 confpath = os.path.join(path, "conf", var)
1104 if os.path.exists(confpath):
1105 for root, dirs, files in os.walk(confpath):
1106 # get all child files, these are appropriate values
1107 for f in files:
1108 val, sep, end = f.rpartition('.')
1109 if end == 'conf':
1110 possible.append(val)
1111
1112 if possible:
1113 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1114
1115 def findInheritsClass(self, klass):
1116 """
1117 Find all recipes which inherit the specified class
1118 """
1119 pkg_list = []
1120
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001121 for pfn in self.recipecaches[''].pkg_fn:
1122 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001123 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001124 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001125
1126 return pkg_list
1127
1128 def generateTargetsTree(self, klass=None, pkgs=None):
1129 """
1130 Generate a dependency tree of buildable targets
1131 Generate an event with the result
1132 """
1133 # if the caller hasn't specified a pkgs list default to universe
1134 if not pkgs:
1135 pkgs = ['universe']
1136 # if inherited_class passed ensure all recipes which inherit the
1137 # specified class are included in pkgs
1138 if klass:
1139 extra_pkgs = self.findInheritsClass(klass)
1140 pkgs = pkgs + extra_pkgs
1141
1142 # generate a dependency tree for all our packages
1143 tree = self.generatePkgDepTreeData(pkgs, 'build')
1144 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1145
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001146 def interactiveMode( self ):
1147 """Drop off into a shell"""
1148 try:
1149 from bb import shell
1150 except ImportError:
1151 parselog.exception("Interactive mode not available")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001152 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001153 else:
1154 shell.start( self )
1155
1156
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001157 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001158 """Handle collections"""
1159 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001160 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001161 if collections:
1162 collection_priorities = {}
1163 collection_depends = {}
1164 collection_list = collections.split()
1165 min_prio = 0
1166 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001167 bb.debug(1,'Processing %s in collection list' % (c))
1168
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001169 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001170 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001171 if priority:
1172 try:
1173 prio = int(priority)
1174 except ValueError:
1175 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1176 errors = True
1177 if min_prio == 0 or prio < min_prio:
1178 min_prio = prio
1179 collection_priorities[c] = prio
1180 else:
1181 collection_priorities[c] = None
1182
1183 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001184 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001185 if deps:
1186 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001187 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001188 except bb.utils.VersionStringException as vse:
1189 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001190 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001191 if dep in collection_list:
1192 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001193 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001194 (op, depver) = opstr.split()
1195 if layerver:
1196 try:
1197 res = bb.utils.vercmp_string_op(layerver, depver, op)
1198 except bb.utils.VersionStringException as vse:
1199 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1200 if not res:
1201 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)
1202 errors = True
1203 else:
1204 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)
1205 errors = True
1206 else:
1207 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1208 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001209 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001210 else:
1211 collection_depends[c] = []
1212
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001213 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001214 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001215 if recs:
1216 try:
1217 recDict = bb.utils.explode_dep_versions2(recs)
1218 except bb.utils.VersionStringException as vse:
1219 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1220 for rec, oplist in list(recDict.items()):
1221 if rec in collection_list:
1222 if oplist:
1223 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001224 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001225 if layerver:
1226 (op, recver) = opstr.split()
1227 try:
1228 res = bb.utils.vercmp_string_op(layerver, recver, op)
1229 except bb.utils.VersionStringException as vse:
1230 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1231 if not res:
1232 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)
1233 continue
1234 else:
1235 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)
1236 continue
1237 parselog.debug(3,"Layer '%s' recommends layer '%s', so we are adding it", c, rec)
1238 collection_depends[c].append(rec)
1239 else:
1240 parselog.debug(3,"Layer '%s' recommends layer '%s', but this layer is not enabled in your configuration", c, rec)
1241
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001242 # Recursively work out collection priorities based on dependencies
1243 def calc_layer_priority(collection):
1244 if not collection_priorities[collection]:
1245 max_depprio = min_prio
1246 for dep in collection_depends[collection]:
1247 calc_layer_priority(dep)
1248 depprio = collection_priorities[dep]
1249 if depprio > max_depprio:
1250 max_depprio = depprio
1251 max_depprio += 1
1252 parselog.debug(1, "Calculated priority of layer %s as %d", collection, max_depprio)
1253 collection_priorities[collection] = max_depprio
1254
1255 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1256 for c in collection_list:
1257 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001258 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001259 if regex is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001260 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1261 errors = True
1262 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001263 elif regex == "":
1264 parselog.debug(1, "BBFILE_PATTERN_%s is empty" % c)
Brad Bishop19323692019-04-05 15:28:33 -04001265 cre = re.compile('^NULL$')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001266 errors = False
1267 else:
1268 try:
1269 cre = re.compile(regex)
1270 except re.error:
1271 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1272 errors = True
1273 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001274 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001275 if errors:
1276 # We've already printed the actual error(s)
1277 raise CollectionError("Errors during parsing layer configuration")
1278
1279 def buildSetVars(self):
1280 """
1281 Setup any variables needed before starting a build
1282 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001283 t = time.gmtime()
1284 for mc in self.databuilder.mcdata:
1285 ds = self.databuilder.mcdata[mc]
1286 if not ds.getVar("BUILDNAME", False):
1287 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1288 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1289 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1290 ds.setVar("TIME", time.strftime('%H%M%S', t))
1291
1292 def reset_mtime_caches(self):
1293 """
1294 Reset mtime caches - this is particularly important when memory resident as something
1295 which is cached is not unlikely to have changed since the last invocation (e.g. a
1296 file associated with a recipe might have been modified by the user).
1297 """
1298 build.reset_cache()
1299 bb.fetch._checksum_cache.mtime_cache.clear()
1300 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1301 if siggen_cache:
1302 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001303
Andrew Geissler5a43b432020-06-13 10:46:56 -05001304 def matchFiles(self, bf, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001305 """
1306 Find the .bb files which match the expression in 'buildfile'.
1307 """
1308 if bf.startswith("/") or bf.startswith("../"):
1309 bf = os.path.abspath(bf)
1310
Andrew Geissler5a43b432020-06-13 10:46:56 -05001311 self.collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)}
1312 filelist, masked, searchdirs = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001313 try:
1314 os.stat(bf)
1315 bf = os.path.abspath(bf)
1316 return [bf]
1317 except OSError:
1318 regexp = re.compile(bf)
1319 matches = []
1320 for f in filelist:
1321 if regexp.search(f) and os.path.isfile(f):
1322 matches.append(f)
1323 return matches
1324
Andrew Geissler5a43b432020-06-13 10:46:56 -05001325 def matchFile(self, buildfile, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001326 """
1327 Find the .bb file which matches the expression in 'buildfile'.
1328 Raise an error if multiple files
1329 """
Andrew Geissler5a43b432020-06-13 10:46:56 -05001330 matches = self.matchFiles(buildfile, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001331 if len(matches) != 1:
1332 if matches:
1333 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1334 if matches:
1335 for f in matches:
1336 msg += "\n %s" % f
1337 parselog.error(msg)
1338 else:
1339 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1340 raise NoSpecificMatch
1341 return matches[0]
1342
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001343 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001344 """
1345 Build the file matching regexp buildfile
1346 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001347 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001348
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001349 # Too many people use -b because they think it's how you normally
1350 # specify a target to be built, so show a warning
1351 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1352
1353 self.buildFileInternal(buildfile, task)
1354
1355 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1356 """
1357 Build the file matching regexp buildfile
1358 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001359
1360 # Parse the configuration here. We need to do it explicitly here since
1361 # buildFile() doesn't use the cache
1362 self.parseConfiguration()
1363
1364 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001365 if task is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001366 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001367 if not task.startswith("do_"):
1368 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001369
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001370 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001371 fn = self.matchFile(fn, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001372
1373 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001374 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001375
Andrew Geissler5a43b432020-06-13 10:46:56 -05001376 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001377
Andrew Geissler5a43b432020-06-13 10:46:56 -05001378 infos = bb_caches[mc].parse(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001379 infos = dict(infos)
1380
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001381 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001382 try:
1383 info_array = infos[fn]
1384 except KeyError:
1385 bb.fatal("%s does not exist" % fn)
1386
1387 if info_array[0].skipped:
1388 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1389
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001390 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001391
1392 # Tweak some variables
1393 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001394 self.recipecaches[mc].ignored_dependencies = set()
1395 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001396 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001397
1398 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001399 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1400 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001401 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1402 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001403
1404 # Invalidate task for target if force mode active
1405 if self.configuration.force:
1406 logger.verbose("Invalidate task %s, %s", task, fn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001407 bb.parse.siggen.invalidate_task(task, self.recipecaches[mc], fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001408
1409 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001410 taskdata = {}
1411 taskdata[mc] = bb.taskdata.TaskData(self.configuration.abort)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001412 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001413
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001414 if quietlog:
1415 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1416 bb.runqueue.logger.setLevel(logging.WARNING)
1417
1418 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1419 if fireevents:
1420 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001421
1422 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001423 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001424
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001425 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001426
1427 def buildFileIdle(server, rq, abort):
1428
1429 msg = None
1430 interrupted = 0
1431 if abort or self.state == state.forceshutdown:
1432 rq.finish_runqueue(True)
1433 msg = "Forced shutdown"
1434 interrupted = 2
1435 elif self.state == state.shutdown:
1436 rq.finish_runqueue(False)
1437 msg = "Stopped build"
1438 interrupted = 1
1439 failures = 0
1440 try:
1441 retval = rq.execute_runqueue()
1442 except runqueue.TaskFailure as exc:
1443 failures += len(exc.args)
1444 retval = False
1445 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001446 self.command.finishAsyncCommand(str(exc))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001447 if quietlog:
1448 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001449 return False
1450
1451 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001452 if fireevents:
1453 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 -05001454 self.command.finishAsyncCommand(msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001455 # We trashed self.recipecaches above
1456 self.parsecache_valid = False
1457 self.configuration.limited_deps = False
1458 bb.parse.siggen.reset(self.data)
1459 if quietlog:
1460 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001461 return False
1462 if retval is True:
1463 return True
1464 return retval
1465
Andrew Geissler635e0e42020-08-21 15:58:33 -05001466 self.idleCallBackRegister(buildFileIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001467
1468 def buildTargets(self, targets, task):
1469 """
1470 Attempt to build the targets specified
1471 """
1472
1473 def buildTargetsIdle(server, rq, abort):
1474 msg = None
1475 interrupted = 0
1476 if abort or self.state == state.forceshutdown:
1477 rq.finish_runqueue(True)
1478 msg = "Forced shutdown"
1479 interrupted = 2
1480 elif self.state == state.shutdown:
1481 rq.finish_runqueue(False)
1482 msg = "Stopped build"
1483 interrupted = 1
1484 failures = 0
1485 try:
1486 retval = rq.execute_runqueue()
1487 except runqueue.TaskFailure as exc:
1488 failures += len(exc.args)
1489 retval = False
1490 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001491 self.command.finishAsyncCommand(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001492 return False
1493
1494 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001495 try:
1496 for mc in self.multiconfigs:
1497 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1498 finally:
1499 self.command.finishAsyncCommand(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001500 return False
1501 if retval is True:
1502 return True
1503 return retval
1504
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001505 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001506 self.buildSetVars()
1507
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001508 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001509 if task is None:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001510 task = self.configuration.cmd
1511
1512 if not task.startswith("do_"):
1513 task = "do_%s" % task
1514
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001515 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1516
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001517 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001518
1519 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.abort)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001520
1521 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001522
1523 # make targets to always look as <target>:do_<task>
1524 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001525 for target in runlist:
1526 if target[0]:
Brad Bishop15ae2502019-06-18 21:44:24 -04001527 ntargets.append("mc:%s:%s:%s" % (target[0], target[1], target[2]))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001528 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001529
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001530 for mc in self.multiconfigs:
1531 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001532
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001533 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001534 if 'universe' in targets:
1535 rq.rqdata.warn_multi_bb = True
1536
Andrew Geissler635e0e42020-08-21 15:58:33 -05001537 self.idleCallBackRegister(buildTargetsIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001538
1539
1540 def getAllKeysWithFlags(self, flaglist):
1541 dump = {}
1542 for k in self.data.keys():
1543 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001544 expand = True
1545 flags = self.data.getVarFlags(k)
1546 if flags and "func" in flags and "python" in flags:
1547 expand = False
1548 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001549 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1550 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001551 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001552 'history' : self.data.varhistory.variable(k),
1553 }
1554 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001555 if flags and d in flags:
1556 dump[k][d] = flags[d]
1557 else:
1558 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001559 except Exception as e:
1560 print(e)
1561 return dump
1562
1563
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001564 def updateCacheSync(self):
1565 if self.state == state.running:
1566 return
1567
1568 # reload files for which we got notifications
1569 for p in self.inotify_modified_files:
1570 bb.parse.update_cache(p)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001571 if p in bb.parse.BBHandler.cached_statements:
1572 del bb.parse.BBHandler.cached_statements[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001573 self.inotify_modified_files = []
1574
1575 if not self.baseconfig_valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001576 logger.debug("Reloading base configuration data")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001577 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001578 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001579
1580 # This is called for all async commands when self.state != running
1581 def updateCache(self):
1582 if self.state == state.running:
1583 return
1584
1585 if self.state in (state.shutdown, state.forceshutdown, state.error):
1586 if hasattr(self.parser, 'shutdown'):
1587 self.parser.shutdown(clean=False, force = True)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001588 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001589 raise bb.BBHandledException()
1590
1591 if self.state != state.parsing:
1592 self.updateCacheSync()
1593
1594 if self.state != state.parsing and not self.parsecache_valid:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001595 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001596 self.parseConfiguration ()
1597 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001598 for mc in self.multiconfigs:
1599 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001600
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001601 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001602 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001603 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001604
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001605 for dep in self.configuration.extra_assume_provided:
1606 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001607
Andrew Geissler5a43b432020-06-13 10:46:56 -05001608 self.collections = {}
1609
1610 mcfilelist = {}
1611 total_masked = 0
1612 searchdirs = set()
1613 for mc in self.multiconfigs:
1614 self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc)
1615 (filelist, masked, search) = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
1616
1617 mcfilelist[mc] = filelist
1618 total_masked += masked
1619 searchdirs |= set(search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001620
1621 # Add inotify watches for directories searched for bb/bbappend files
1622 for dirent in searchdirs:
1623 self.add_filewatch([[dirent]], dirs=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001624
Andrew Geissler5a43b432020-06-13 10:46:56 -05001625 self.parser = CookerParser(self, mcfilelist, total_masked)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001626 self.parsecache_valid = True
1627
1628 self.state = state.parsing
1629
1630 if not self.parser.parse_next():
1631 collectlog.debug(1, "parsing complete")
1632 if self.parser.error:
1633 raise bb.BBHandledException()
1634 self.show_appends_with_no_recipes()
1635 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001636 for mc in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001637 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 -05001638 self.state = state.running
1639
1640 # Send an event listing all stamps reachable after parsing
1641 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001642 for mc in self.multiconfigs:
1643 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1644 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001645 return None
1646
1647 return True
1648
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001649 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001650
1651 # Return a copy, don't modify the original
1652 pkgs_to_build = pkgs_to_build[:]
1653
1654 if len(pkgs_to_build) == 0:
1655 raise NothingToBuild
1656
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001657 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001658 for pkg in pkgs_to_build.copy():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001659 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001660 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Brad Bishop15ae2502019-06-18 21:44:24 -04001661 if pkg.startswith("multiconfig:"):
1662 pkgs_to_build.remove(pkg)
1663 pkgs_to_build.append(pkg.replace("multiconfig:", "mc:"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001664
1665 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001666 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001667 for mc in self.multiconfigs:
1668 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1669 for t in self.recipecaches[mc].world_target:
1670 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001671 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001672 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001673
1674 if 'universe' in pkgs_to_build:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001675 parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001676 parselog.debug(1, "collating packages for \"universe\"")
1677 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001678 for mc in self.multiconfigs:
1679 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001680 if task:
1681 foundtask = False
1682 for provider_fn in self.recipecaches[mc].providers[t]:
1683 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1684 foundtask = True
1685 break
1686 if not foundtask:
1687 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1688 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001689 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001690 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001691 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001692
1693 return pkgs_to_build
1694
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001695 def pre_serve(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001696 return
1697
1698 def post_serve(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001699 self.shutdown(force=True)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001700 prserv.serv.auto_shutdown()
Brad Bishop08902b02019-08-20 09:16:51 -04001701 if self.hashserv:
1702 self.hashserv.process.terminate()
1703 self.hashserv.process.join()
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001704 if hasattr(self, "data"):
1705 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001706
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001707 def shutdown(self, force = False):
1708 if force:
1709 self.state = state.forceshutdown
1710 else:
1711 self.state = state.shutdown
1712
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001713 if self.parser:
1714 self.parser.shutdown(clean=not force, force=force)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001715 self.parser.final_cleanup()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001716
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001717 def finishcommand(self):
1718 self.state = state.initial
1719
1720 def reset(self):
1721 self.initConfigurationData()
Brad Bishop08902b02019-08-20 09:16:51 -04001722 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001723
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001724 def clientComplete(self):
1725 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001726 self.finishcommand()
1727 self.extraconfigdata = {}
1728 self.command.reset()
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001729 if hasattr(self, "data"):
1730 self.databuilder.reset()
1731 self.data = self.databuilder.data
Andrew Geissler82c905d2020-04-13 13:39:40 -05001732 self.parsecache_valid = False
1733 self.baseconfig_valid = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001734
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001735
1736class CookerExit(bb.event.Event):
1737 """
1738 Notify clients of the Cooker shutdown
1739 """
1740
1741 def __init__(self):
1742 bb.event.Event.__init__(self)
1743
1744
1745class CookerCollectFiles(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001746 def __init__(self, priorities, mc=''):
1747 self.mc = mc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001748 self.bbappends = []
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001749 # Priorities is a list of tupples, with the second element as the pattern.
1750 # We need to sort the list with the longest pattern first, and so on to
1751 # the shortest. This allows nested layers to be properly evaluated.
1752 self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001753
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001754 def calc_bbfile_priority(self, filename):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001755 for _, _, regex, pri in self.bbfile_config_priorities:
1756 if regex.match(filename):
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001757 return pri, regex
1758 return 0, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001759
1760 def get_bbfiles(self):
1761 """Get list of default .bb files by reading out the current directory"""
1762 path = os.getcwd()
1763 contents = os.listdir(path)
1764 bbfiles = []
1765 for f in contents:
1766 if f.endswith(".bb"):
1767 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1768 return bbfiles
1769
1770 def find_bbfiles(self, path):
1771 """Find all the .bb and .bbappend files in a directory"""
1772 found = []
1773 for dir, dirs, files in os.walk(path):
1774 for ignored in ('SCCS', 'CVS', '.svn'):
1775 if ignored in dirs:
1776 dirs.remove(ignored)
1777 found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
1778
1779 return found
1780
1781 def collect_bbfiles(self, config, eventdata):
1782 """Collect all available .bb build files"""
1783 masked = 0
1784
1785 collectlog.debug(1, "collecting .bb files")
1786
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001787 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001788
1789 # Sort files by priority
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001790 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem)[0] )
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001791 config.setVar("BBFILES_PRIORITIZED", " ".join(files))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001792
1793 if not len(files):
1794 files = self.get_bbfiles()
1795
1796 if not len(files):
1797 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1798 bb.event.fire(CookerExit(), eventdata)
1799
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001800 # We need to track where we look so that we can add inotify watches. There
1801 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001802 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001803 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001804 if hasattr(os, 'scandir'):
1805 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001806 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001807
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001808 def ourlistdir(d):
1809 searchdirs.append(d)
1810 return origlistdir(d)
1811
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001812 def ourscandir(d):
1813 searchdirs.append(d)
1814 return origscandir(d)
1815
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001816 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001817 if hasattr(os, 'scandir'):
1818 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001819 try:
1820 # Can't use set here as order is important
1821 newfiles = []
1822 for f in files:
1823 if os.path.isdir(f):
1824 dirfiles = self.find_bbfiles(f)
1825 for g in dirfiles:
1826 if g not in newfiles:
1827 newfiles.append(g)
1828 else:
1829 globbed = glob.glob(f)
1830 if not globbed and os.path.exists(f):
1831 globbed = [f]
1832 # glob gives files in order on disk. Sort to be deterministic.
1833 for g in sorted(globbed):
1834 if g not in newfiles:
1835 newfiles.append(g)
1836 finally:
1837 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001838 if hasattr(os, 'scandir'):
1839 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001840
1841 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001842
1843 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001844 # First validate the individual regular expressions and ignore any
1845 # that do not compile
1846 bbmasks = []
1847 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001848 # When constructing an older style single regex, it's possible for BBMASK
1849 # to end up beginning with '|', which matches and masks _everything_.
1850 if mask.startswith("|"):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001851 collectlog.warning("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001852 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001853 try:
1854 re.compile(mask)
1855 bbmasks.append(mask)
1856 except sre_constants.error:
1857 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1858
1859 # Then validate the combined regular expressions. This should never
1860 # fail, but better safe than sorry...
1861 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001862 try:
1863 bbmask_compiled = re.compile(bbmask)
1864 except sre_constants.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001865 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1866 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001867
1868 bbfiles = []
1869 bbappend = []
1870 for f in newfiles:
1871 if bbmask and bbmask_compiled.search(f):
1872 collectlog.debug(1, "skipping masked file %s", f)
1873 masked += 1
1874 continue
1875 if f.endswith('.bb'):
1876 bbfiles.append(f)
1877 elif f.endswith('.bbappend'):
1878 bbappend.append(f)
1879 else:
1880 collectlog.debug(1, "skipping %s: unknown file extension", f)
1881
1882 # Build a list of .bbappend files for each .bb file
1883 for f in bbappend:
1884 base = os.path.basename(f).replace('.bbappend', '.bb')
1885 self.bbappends.append((base, f))
1886
1887 # Find overlayed recipes
1888 # bbfiles will be in priority order which makes this easy
1889 bbfile_seen = dict()
1890 self.overlayed = defaultdict(list)
1891 for f in reversed(bbfiles):
1892 base = os.path.basename(f)
1893 if base not in bbfile_seen:
1894 bbfile_seen[base] = f
1895 else:
1896 topfile = bbfile_seen[base]
1897 self.overlayed[topfile].append(f)
1898
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001899 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001900
1901 def get_file_appends(self, fn):
1902 """
1903 Returns a list of .bbappend files to apply to fn
1904 """
1905 filelist = []
1906 f = os.path.basename(fn)
1907 for b in self.bbappends:
1908 (bbappend, filename) = b
1909 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1910 filelist.append(filename)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001911 return tuple(filelist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001912
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001913 def collection_priorities(self, pkgfns, fns, d):
1914 # Return the priorities of the entries in pkgfns
1915 # Also check that all the regexes in self.bbfile_config_priorities are used
1916 # (but to do that we need to ensure skipped recipes aren't counted, nor
1917 # collections in BBFILE_PATTERN_IGNORE_EMPTY)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001918
1919 priorities = {}
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001920 seen = set()
1921 matched = set()
1922
1923 matched_regex = set()
1924 unmatched_regex = set()
1925 for _, _, regex, _ in self.bbfile_config_priorities:
1926 unmatched_regex.add(regex)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001927
1928 # Calculate priorities for each file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001929 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001930 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001931 priorities[p], regex = self.calc_bbfile_priority(realfn)
1932 if regex in unmatched_regex:
1933 matched_regex.add(regex)
1934 unmatched_regex.remove(regex)
1935 seen.add(realfn)
1936 if regex:
1937 matched.add(realfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001938
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001939 if unmatched_regex:
1940 # Account for bbappend files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001941 for b in self.bbappends:
1942 (bbfile, append) = b
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001943 seen.add(append)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001944
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001945 # Account for skipped recipes
1946 seen.update(fns)
1947
1948 seen.difference_update(matched)
1949
1950 def already_matched(fn):
1951 for regex in matched_regex:
1952 if regex.match(fn):
1953 return True
1954 return False
1955
1956 for unmatch in unmatched_regex.copy():
1957 for fn in seen:
1958 if unmatch.match(fn):
1959 # If the bbappend or file was already matched by another regex, skip it
1960 # e.g. for a layer within a layer, the outer regex could match, the inner
1961 # regex may match nothing and we should warn about that
1962 if already_matched(fn):
1963 continue
1964 unmatched_regex.remove(unmatch)
1965 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001966
1967 for collection, pattern, regex, _ in self.bbfile_config_priorities:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001968 if regex in unmatched_regex:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001969 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Andrew Geissler5a43b432020-06-13 10:46:56 -05001970 collectlog.warning("No bb files in %s matched BBFILE_PATTERN_%s '%s'" % (self.mc if self.mc else 'default',
1971 collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001972
1973 return priorities
1974
1975class ParsingFailure(Exception):
1976 def __init__(self, realexception, recipe):
1977 self.realexception = realexception
1978 self.recipe = recipe
1979 Exception.__init__(self, realexception, recipe)
1980
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001981class Parser(multiprocessing.Process):
1982 def __init__(self, jobs, results, quit, init, profile):
1983 self.jobs = jobs
1984 self.results = results
1985 self.quit = quit
1986 self.init = init
1987 multiprocessing.Process.__init__(self)
1988 self.context = bb.utils.get_context().copy()
1989 self.handlers = bb.event.get_class_handlers().copy()
1990 self.profile = profile
1991
1992 def run(self):
1993
1994 if not self.profile:
1995 self.realrun()
1996 return
1997
1998 try:
1999 import cProfile as profile
2000 except:
2001 import profile
2002 prof = profile.Profile()
2003 try:
2004 profile.Profile.runcall(prof, self.realrun)
2005 finally:
2006 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
2007 prof.dump_stats(logfile)
2008
2009 def realrun(self):
2010 if self.init:
2011 self.init()
2012
2013 pending = []
2014 while True:
2015 try:
2016 self.quit.get_nowait()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002017 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002018 pass
2019 else:
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002020 self.results.close()
2021 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002022 break
2023
2024 if pending:
2025 result = pending.pop()
2026 else:
2027 try:
Brad Bishop19323692019-04-05 15:28:33 -04002028 job = self.jobs.pop()
2029 except IndexError:
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002030 self.results.close()
2031 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002032 break
2033 result = self.parse(*job)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002034 # Clear the siggen cache after parsing to control memory usage, its huge
2035 bb.parse.siggen.postparsing_clean_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002036 try:
2037 self.results.put(result, timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002038 except queue.Full:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002039 pending.append(result)
2040
Andrew Geissler5a43b432020-06-13 10:46:56 -05002041 def parse(self, mc, cache, filename, appends):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002042 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002043 origfilter = bb.event.LogHandler.filter
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002044 # Record the filename we're parsing into any events generated
2045 def parse_filter(self, record):
2046 record.taskpid = bb.event.worker_pid
2047 record.fn = filename
2048 return True
2049
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002050 # Reset our environment and handlers to the original settings
2051 bb.utils.set_context(self.context.copy())
2052 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002053 bb.event.LogHandler.filter = parse_filter
2054
Andrew Geissler5a43b432020-06-13 10:46:56 -05002055 return True, mc, cache.parse(filename, appends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002056 except Exception as exc:
2057 tb = sys.exc_info()[2]
2058 exc.recipe = filename
2059 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
2060 return True, exc
2061 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
2062 # and for example a worker thread doesn't just exit on its own in response to
2063 # a SystemExit event for example.
2064 except BaseException as exc:
2065 return True, ParsingFailure(exc, filename)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002066 finally:
2067 bb.event.LogHandler.filter = origfilter
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002068
2069class CookerParser(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002070 def __init__(self, cooker, mcfilelist, masked):
2071 self.mcfilelist = mcfilelist
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002072 self.cooker = cooker
2073 self.cfgdata = cooker.data
2074 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002075 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002076
2077 # Accounting statistics
2078 self.parsed = 0
2079 self.cached = 0
2080 self.error = 0
2081 self.masked = masked
2082
2083 self.skipped = 0
2084 self.virtuals = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002085
2086 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002087 self.process_names = []
2088
Andrew Geissler5a43b432020-06-13 10:46:56 -05002089 self.bb_caches = bb.cache.MulticonfigCache(self.cfgbuilder, self.cfghash, cooker.caches_array)
2090 self.fromcache = set()
2091 self.willparse = set()
2092 for mc in self.cooker.multiconfigs:
2093 for filename in self.mcfilelist[mc]:
2094 appends = self.cooker.collections[mc].get_file_appends(filename)
2095 if not self.bb_caches[mc].cacheValid(filename, appends):
2096 self.willparse.add((mc, self.bb_caches[mc], filename, appends))
2097 else:
2098 self.fromcache.add((mc, self.bb_caches[mc], filename, appends))
2099
2100 self.total = len(self.fromcache) + len(self.willparse)
2101 self.toparse = len(self.willparse)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002102 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002103
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002104 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Andrew Geissler5a43b432020-06-13 10:46:56 -05002105 multiprocessing.cpu_count()), self.toparse)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002106
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002107 self.start()
2108 self.haveshutdown = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002109 self.syncthread = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002110
2111 def start(self):
2112 self.results = self.load_cached()
2113 self.processes = []
2114 if self.toparse:
2115 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
2116 def init():
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002117 signal.signal(signal.SIGTERM, signal.SIG_DFL)
2118 signal.signal(signal.SIGHUP, signal.SIG_DFL)
2119 signal.signal(signal.SIGINT, signal.SIG_IGN)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002120 bb.utils.set_process_name(multiprocessing.current_process().name)
2121 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2122 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002123
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002124 self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002125 self.result_queue = multiprocessing.Queue()
Brad Bishop19323692019-04-05 15:28:33 -04002126
2127 def chunkify(lst,n):
2128 return [lst[i::n] for i in range(n)]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002129 self.jobs = chunkify(list(self.willparse), self.num_processes)
Brad Bishop19323692019-04-05 15:28:33 -04002130
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002131 for i in range(0, self.num_processes):
Brad Bishop19323692019-04-05 15:28:33 -04002132 parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, init, self.cooker.configuration.profile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002133 parser.start()
2134 self.process_names.append(parser.name)
2135 self.processes.append(parser)
2136
2137 self.results = itertools.chain(self.results, self.parse_generator())
2138
2139 def shutdown(self, clean=True, force=False):
2140 if not self.toparse:
2141 return
2142 if self.haveshutdown:
2143 return
2144 self.haveshutdown = True
2145
2146 if clean:
2147 event = bb.event.ParseCompleted(self.cached, self.parsed,
2148 self.skipped, self.masked,
2149 self.virtuals, self.error,
2150 self.total)
2151
2152 bb.event.fire(event, self.cfgdata)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002153
2154 for process in self.processes:
2155 self.parser_quit.put(None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002156
Brad Bishop08902b02019-08-20 09:16:51 -04002157 # Cleanup the queue before call process.join(), otherwise there might be
2158 # deadlocks.
2159 while True:
2160 try:
2161 self.result_queue.get(timeout=0.25)
2162 except queue.Empty:
2163 break
2164
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002165 for process in self.processes:
2166 if force:
2167 process.join(.1)
2168 process.terminate()
2169 else:
2170 process.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002171
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002172 self.parser_quit.close()
2173 # Allow data left in the cancel queue to be discarded
2174 self.parser_quit.cancel_join_thread()
2175
Andrew Geissler5a43b432020-06-13 10:46:56 -05002176 def sync_caches():
2177 for c in self.bb_caches.values():
2178 c.sync()
2179
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002180 sync = threading.Thread(target=sync_caches, name="SyncThread")
2181 self.syncthread = sync
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002182 sync.start()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002183 bb.codeparser.parser_cache_savemerge()
2184 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002185 if self.cooker.configuration.profile:
2186 profiles = []
2187 for i in self.process_names:
2188 logfile = "profile-parse-%s.log" % i
2189 if os.path.exists(logfile):
2190 profiles.append(logfile)
2191
2192 pout = "profile-parse.log.processed"
2193 bb.utils.process_profilelog(profiles, pout = pout)
2194 print("Processed parsing statistics saved to %s" % (pout))
2195
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002196 def final_cleanup(self):
2197 if self.syncthread:
2198 self.syncthread.join()
2199
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002200 def load_cached(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002201 for mc, cache, filename, appends in self.fromcache:
2202 cached, infos = cache.load(filename, appends)
2203 yield not cached, mc, infos
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002204
2205 def parse_generator(self):
2206 while True:
2207 if self.parsed >= self.toparse:
2208 break
2209
2210 try:
2211 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002212 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002213 pass
2214 else:
2215 value = result[1]
2216 if isinstance(value, BaseException):
2217 raise value
2218 else:
2219 yield result
2220
2221 def parse_next(self):
2222 result = []
2223 parsed = None
2224 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002225 parsed, mc, result = next(self.results)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002226 except StopIteration:
2227 self.shutdown()
2228 return False
2229 except bb.BBHandledException as exc:
2230 self.error += 1
2231 logger.error('Failed to parse recipe: %s' % exc.recipe)
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002232 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002233 return False
2234 except ParsingFailure as exc:
2235 self.error += 1
2236 logger.error('Unable to parse %s: %s' %
2237 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002238 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002239 return False
2240 except bb.parse.ParseError as exc:
2241 self.error += 1
2242 logger.error(str(exc))
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002243 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002244 return False
2245 except bb.data_smart.ExpansionError as exc:
2246 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002247 bbdir = os.path.dirname(__file__) + os.sep
2248 etype, value, _ = sys.exc_info()
2249 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2250 logger.error('ExpansionError during parsing %s', value.recipe,
2251 exc_info=(etype, value, tb))
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002252 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002253 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002254 except Exception as exc:
2255 self.error += 1
2256 etype, value, tb = sys.exc_info()
2257 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002258 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002259 exc_info=(etype, value, exc.traceback))
2260 else:
2261 # Most likely, an exception occurred during raising an exception
2262 import traceback
2263 logger.error('Exception during parse: %s' % traceback.format_exc())
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002264 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002265 return False
2266
2267 self.current += 1
2268 self.virtuals += len(result)
2269 if parsed:
2270 self.parsed += 1
2271 if self.parsed % self.progress_chunk == 0:
2272 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2273 self.cfgdata)
2274 else:
2275 self.cached += 1
2276
2277 for virtualfn, info_array in result:
2278 if info_array[0].skipped:
2279 self.skipped += 1
2280 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Andrew Geissler5a43b432020-06-13 10:46:56 -05002281 self.bb_caches[mc].add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002282 parsed=parsed, watcher = self.cooker.add_filewatch)
2283 return True
2284
2285 def reparse(self, filename):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002286 to_reparse = set()
2287 for mc in self.cooker.multiconfigs:
2288 to_reparse.add((mc, filename, self.cooker.collections[mc].get_file_appends(filename)))
2289
2290 for mc, filename, appends in to_reparse:
2291 infos = self.bb_caches[mc].parse(filename, appends)
2292 for vfn, info_array in infos:
2293 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)