blob: 08593d114a6b91566674b49f893bf3e5c834b7e4 [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
Patrick Williams45852732022-04-02 08:58:32 -0500162 self.orig_syspath = sys.path.copy()
163 self.orig_sysmodules = [*sys.modules]
164
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500165 self.configuration = bb.cookerdata.CookerConfiguration()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500166
Andrew Geissler635e0e42020-08-21 15:58:33 -0500167 self.idleCallBackRegister = idleCallBackRegister
168
Brad Bishopf058f492019-01-28 23:50:33 -0500169 bb.debug(1, "BBCooker starting %s" % time.time())
170 sys.stdout.flush()
171
Patrick Williamsde0582f2022-04-08 10:23:27 -0500172 self.configwatcher = None
173 self.confignotifier = None
Brad Bishopf058f492019-01-28 23:50:33 -0500174
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500175 self.watchmask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CREATE | pyinotify.IN_DELETE | \
176 pyinotify.IN_DELETE_SELF | pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF | \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500177 pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500178
Patrick Williamsde0582f2022-04-08 10:23:27 -0500179 self.watcher = None
180 self.notifier = None
Brad Bishopf058f492019-01-28 23:50:33 -0500181
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500182 # If being called by something like tinfoil, we need to clean cached data
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500183 # which may now be invalid
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500184 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500185 bb.parse.BBHandler.cached_statements = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500186
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500187 self.ui_cmdline = None
Brad Bishop08902b02019-08-20 09:16:51 -0400188 self.hashserv = None
Brad Bishopa34c0302019-09-23 22:34:48 -0400189 self.hashservaddr = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500190
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500191 self.inotify_modified_files = []
192
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000193 def _process_inotify_updates(server, cooker, halt):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500194 cooker.process_inotify_updates()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195 return 1.0
196
Andrew Geissler635e0e42020-08-21 15:58:33 -0500197 self.idleCallBackRegister(_process_inotify_updates, self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500198
199 # TOSTOP must not be set or our children will hang when they output
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600200 try:
201 fd = sys.stdout.fileno()
202 if os.isatty(fd):
203 import termios
204 tcattr = termios.tcgetattr(fd)
205 if tcattr[3] & termios.TOSTOP:
206 buildlog.info("The terminal had the TOSTOP bit set, clearing...")
207 tcattr[3] = tcattr[3] & ~termios.TOSTOP
208 termios.tcsetattr(fd, termios.TCSANOW, tcattr)
209 except UnsupportedOperation:
210 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500211
212 self.command = bb.command.Command(self)
213 self.state = state.initial
214
215 self.parser = None
216
217 signal.signal(signal.SIGTERM, self.sigterm_exception)
218 # Let SIGHUP exit as SIGTERM
219 signal.signal(signal.SIGHUP, self.sigterm_exception)
220
Brad Bishopf058f492019-01-28 23:50:33 -0500221 bb.debug(1, "BBCooker startup complete %s" % time.time())
222 sys.stdout.flush()
223
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500224 def init_configdata(self):
225 if not hasattr(self, "data"):
226 self.initConfigurationData()
227 bb.debug(1, "BBCooker parsed base configuration %s" % time.time())
228 sys.stdout.flush()
229 self.handlePRServ()
230
Patrick Williamsde0582f2022-04-08 10:23:27 -0500231 def setupConfigWatcher(self):
232 if self.configwatcher:
233 self.configwatcher.close()
234 self.confignotifier = None
235 self.configwatcher = None
236 self.configwatcher = pyinotify.WatchManager()
237 self.configwatcher.bbseen = set()
238 self.configwatcher.bbwatchedfiles = set()
239 self.confignotifier = pyinotify.Notifier(self.configwatcher, self.config_notifications)
240
241 def setupParserWatcher(self):
242 if self.watcher:
243 self.watcher.close()
244 self.notifier = None
245 self.watcher = None
246 self.watcher = pyinotify.WatchManager()
247 self.watcher.bbseen = set()
248 self.watcher.bbwatchedfiles = set()
249 self.notifier = pyinotify.Notifier(self.watcher, self.notifications)
250
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500251 def process_inotify_updates(self):
252 for n in [self.confignotifier, self.notifier]:
Patrick Williamsde0582f2022-04-08 10:23:27 -0500253 if n and n.check_events(timeout=0):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500254 # read notified events and enqeue them
255 n.read_events()
256 n.process_events()
257
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500258 def config_notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500259 if event.maskname == "IN_Q_OVERFLOW":
260 bb.warn("inotify event queue overflowed, invalidating caches.")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500261 self.parsecache_valid = False
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500262 self.baseconfig_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500263 bb.parse.clear_cache()
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500264 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500265 if not event.pathname in self.configwatcher.bbwatchedfiles:
266 return
Andrew Geissler9aee5002022-03-30 16:27:02 +0000267 if "IN_ISDIR" in event.maskname:
Patrick Williams45852732022-04-02 08:58:32 -0500268 if "IN_CREATE" in event.maskname or "IN_DELETE" in event.maskname:
269 if event.pathname in self.configwatcher.bbseen:
270 self.configwatcher.bbseen.remove(event.pathname)
271 # Could remove all entries starting with the directory but for now...
272 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500273 if not event.pathname in self.inotify_modified_files:
274 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500275 self.baseconfig_valid = False
276
277 def notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500278 if event.maskname == "IN_Q_OVERFLOW":
279 bb.warn("inotify event queue overflowed, invalidating caches.")
280 self.parsecache_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500281 bb.parse.clear_cache()
282 return
283 if event.pathname.endswith("bitbake-cookerdaemon.log") \
284 or event.pathname.endswith("bitbake.lock"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500285 return
Andrew Geissler9aee5002022-03-30 16:27:02 +0000286 if "IN_ISDIR" in event.maskname:
Patrick Williams45852732022-04-02 08:58:32 -0500287 if "IN_CREATE" in event.maskname or "IN_DELETE" in event.maskname:
288 if event.pathname in self.watcher.bbseen:
289 self.watcher.bbseen.remove(event.pathname)
290 # Could remove all entries starting with the directory but for now...
291 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500292 if not event.pathname in self.inotify_modified_files:
293 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500294 self.parsecache_valid = False
295
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500296 def add_filewatch(self, deps, watcher=None, dirs=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500297 if not watcher:
298 watcher = self.watcher
299 for i in deps:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500300 watcher.bbwatchedfiles.add(i[0])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500301 if dirs:
302 f = i[0]
303 else:
304 f = os.path.dirname(i[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500305 if f in watcher.bbseen:
306 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -0500307 watcher.bbseen.add(f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500308 watchtarget = None
309 while True:
310 # We try and add watches for files that don't exist but if they did, would influence
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500311 # the parser. The parent directory of these files may not exist, in which case we need
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500312 # to watch any parent that does exist for changes.
313 try:
314 watcher.add_watch(f, self.watchmask, quiet=False)
315 if watchtarget:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500316 watcher.bbwatchedfiles.add(watchtarget)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317 break
318 except pyinotify.WatchManagerError as e:
319 if 'ENOENT' in str(e):
320 watchtarget = f
321 f = os.path.dirname(f)
322 if f in watcher.bbseen:
323 break
Andrew Geissler82c905d2020-04-13 13:39:40 -0500324 watcher.bbseen.add(f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500325 continue
326 if 'ENOSPC' in str(e):
327 providerlog.error("No space left on device or exceeds fs.inotify.max_user_watches?")
328 providerlog.error("To check max_user_watches: sysctl -n fs.inotify.max_user_watches.")
329 providerlog.error("To modify max_user_watches: sysctl -n -w fs.inotify.max_user_watches=<value>.")
330 providerlog.error("Root privilege is required to modify max_user_watches.")
331 raise
332
333 def sigterm_exception(self, signum, stackframe):
334 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500335 bb.warn("Cooker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500336 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500337 bb.warn("Cooker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500338 self.state = state.forceshutdown
339
340 def setFeatures(self, features):
341 # we only accept a new feature set if we're in state initial, so we can reset without problems
342 if not self.state in [state.initial, state.shutdown, state.forceshutdown, state.stopped, state.error]:
343 raise Exception("Illegal state for feature set change")
344 original_featureset = list(self.featureset)
345 for feature in features:
346 self.featureset.setFeature(feature)
347 bb.debug(1, "Features set %s (was %s)" % (original_featureset, list(self.featureset)))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500348 if (original_featureset != list(self.featureset)) and self.state != state.error and hasattr(self, "data"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500349 self.reset()
350
351 def initConfigurationData(self):
352
353 self.state = state.initial
354 self.caches_array = []
355
Patrick Williams45852732022-04-02 08:58:32 -0500356 sys.path = self.orig_syspath.copy()
357 for mod in [*sys.modules]:
358 if mod not in self.orig_sysmodules:
359 del sys.modules[mod]
360
Patrick Williamsde0582f2022-04-08 10:23:27 -0500361 self.setupConfigWatcher()
362
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500363 # Need to preserve BB_CONSOLELOG over resets
364 consolelog = None
365 if hasattr(self, "data"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500366 consolelog = self.data.getVar("BB_CONSOLELOG")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500367
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500368 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
369 self.enableDataTracking()
370
371 all_extra_cache_names = []
372 # We hardcode all known cache types in a single place, here.
373 if CookerFeatures.HOB_EXTRA_CACHES in self.featureset:
374 all_extra_cache_names.append("bb.cache_extra:HobRecipeInfo")
375
376 caches_name_array = ['bb.cache:CoreRecipeInfo'] + all_extra_cache_names
377
378 # At least CoreRecipeInfo will be loaded, so caches_array will never be empty!
379 # This is the entry point, no further check needed!
380 for var in caches_name_array:
381 try:
382 module_name, cache_name = var.split(':')
383 module = __import__(module_name, fromlist=(cache_name,))
384 self.caches_array.append(getattr(module, cache_name))
385 except ImportError as exc:
386 logger.critical("Unable to import extra RecipeInfo '%s' from '%s': %s" % (cache_name, module_name, exc))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500387 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500388
389 self.databuilder = bb.cookerdata.CookerDataBuilder(self.configuration, False)
390 self.databuilder.parseBaseConfiguration()
391 self.data = self.databuilder.data
392 self.data_hash = self.databuilder.data_hash
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500393 self.extraconfigdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500394
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500395 if consolelog:
396 self.data.setVar("BB_CONSOLELOG", consolelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500397
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500398 self.data.setVar('BB_CMDLINE', self.ui_cmdline)
399
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500400 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
401 self.disableDataTracking()
402
Brad Bishop15ae2502019-06-18 21:44:24 -0400403 for mc in self.databuilder.mcdata.values():
404 mc.renameVar("__depends", "__base_depends")
405 self.add_filewatch(mc.getVar("__base_depends", False), self.configwatcher)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500406
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500407 self.baseconfig_valid = True
408 self.parsecache_valid = False
409
410 def handlePRServ(self):
411 # Setup a PR Server based on the new configuration
412 try:
413 self.prhost = prserv.serv.auto_start(self.data)
414 except prserv.serv.PRServiceConfigError as e:
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500415 bb.fatal("Unable to start PR Server, exiting, check the bitbake-cookerdaemon.log")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500416
Brad Bishopa34c0302019-09-23 22:34:48 -0400417 if self.data.getVar("BB_HASHSERVE") == "auto":
418 # Create a new hash server bound to a unix domain socket
Brad Bishop08902b02019-08-20 09:16:51 -0400419 if not self.hashserv:
420 dbfile = (self.data.getVar("PERSISTENT_DIR") or self.data.getVar("CACHE")) + "/hashserv.db"
Andrew Geissler595f6302022-01-24 19:11:47 +0000421 upstream = self.data.getVar("BB_HASHSERVE_UPSTREAM") or None
422 if upstream:
423 import socket
424 try:
425 sock = socket.create_connection(upstream.split(":"), 5)
426 sock.close()
427 except socket.error as e:
428 bb.warn("BB_HASHSERVE_UPSTREAM is not valid, unable to connect hash equivalence server at '%s': %s"
429 % (upstream, repr(e)))
430
Brad Bishopa34c0302019-09-23 22:34:48 -0400431 self.hashservaddr = "unix://%s/hashserve.sock" % self.data.getVar("TOPDIR")
Andrew Geissler5199d832021-09-24 16:47:35 -0500432 self.hashserv = hashserv.create_server(
433 self.hashservaddr,
434 dbfile,
435 sync=False,
Andrew Geissler595f6302022-01-24 19:11:47 +0000436 upstream=upstream,
Andrew Geissler5199d832021-09-24 16:47:35 -0500437 )
Patrick Williams213cb262021-08-07 19:21:33 -0500438 self.hashserv.serve_as_process()
Brad Bishopa34c0302019-09-23 22:34:48 -0400439 self.data.setVar("BB_HASHSERVE", self.hashservaddr)
440 self.databuilder.origdata.setVar("BB_HASHSERVE", self.hashservaddr)
441 self.databuilder.data.setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400442 for mc in self.databuilder.mcdata:
Brad Bishopa34c0302019-09-23 22:34:48 -0400443 self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400444
445 bb.parse.init_parser(self.data)
446
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500447 def enableDataTracking(self):
448 self.configuration.tracking = True
449 if hasattr(self, "data"):
450 self.data.enableTracking()
451
452 def disableDataTracking(self):
453 self.configuration.tracking = False
454 if hasattr(self, "data"):
455 self.data.disableTracking()
456
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500457 def parseConfiguration(self):
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600458 self.updateCacheSync()
459
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500460 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500461 nice = self.data.getVar("BB_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500462 if nice:
463 curnice = os.nice(0)
464 nice = int(nice) - curnice
465 buildlog.verbose("Renice to %s " % os.nice(nice))
466
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600467 if self.recipecaches:
468 del self.recipecaches
469 self.multiconfigs = self.databuilder.mcdata.keys()
470 self.recipecaches = {}
471 for mc in self.multiconfigs:
472 self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500473
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500474 self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500475
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500476 self.parsecache_valid = False
477
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500478 def updateConfigOpts(self, options, environment, cmdline):
479 self.ui_cmdline = cmdline
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500480 clean = True
481 for o in options:
482 if o in ['prefile', 'postfile']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500483 # Only these options may require a reparse
484 try:
485 if getattr(self.configuration, o) == options[o]:
486 # Value is the same, no need to mark dirty
487 continue
488 except AttributeError:
489 pass
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600490 logger.debug("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500491 print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500492 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500493 if hasattr(self.configuration, o):
494 setattr(self.configuration, o, options[o])
495
496 if self.configuration.writeeventlog:
497 if self.eventlog and self.eventlog[0] != self.configuration.writeeventlog:
498 bb.event.unregister_UIHhandler(self.eventlog[1])
499 if not self.eventlog or self.eventlog[0] != self.configuration.writeeventlog:
500 # we log all events to a file if so directed
501 # register the log file writer as UI Handler
502 writer = EventWriter(self, self.configuration.writeeventlog)
503 EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
504 self.eventlog = (self.configuration.writeeventlog, bb.event.register_UIHhandler(EventLogWriteHandler(writer)))
505
506 bb.msg.loggerDefaultLogLevel = self.configuration.default_loglevel
507 bb.msg.loggerDefaultDomains = self.configuration.debug_domains
508
509 if hasattr(self, "data"):
510 origenv = bb.data.init()
511 for k in environment:
512 origenv.setVar(k, environment[k])
513 self.data.setVar("BB_ORIGENV", origenv)
514
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500515 for k in bb.utils.approved_variables():
516 if k in environment and k not in self.configuration.env:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600517 logger.debug("Updating new environment variable %s to %s" % (k, environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500518 self.configuration.env[k] = environment[k]
519 clean = False
520 if k in self.configuration.env and k not in environment:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600521 logger.debug("Updating environment variable %s (deleted)" % (k))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500522 del self.configuration.env[k]
523 clean = False
524 if k not in self.configuration.env and k not in environment:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500525 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500526 if environment[k] != self.configuration.env[k]:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600527 logger.debug("Updating environment variable %s from %s to %s" % (k, self.configuration.env[k], environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500528 self.configuration.env[k] = environment[k]
529 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500530
531 # Now update all the variables not in the datastore to match
532 self.configuration.env = environment
533
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534 if not clean:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600535 logger.debug("Base environment change, triggering reparse")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500536 self.reset()
537
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000538 def runCommands(self, server, data, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500539 """
540 Run any queued asynchronous command
541 This is done by the idle handler so it runs in true context rather than
542 tied to any UI.
543 """
544
545 return self.command.runAsyncCommand()
546
547 def showVersions(self):
548
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500549 (latest_versions, preferred_versions, required) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500550
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500551 logger.plain("%-35s %25s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version", "Required Version")
552 logger.plain("%-35s %25s %25s %25s\n", "===========", "==============", "=================", "================")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500553
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500554 for p in sorted(self.recipecaches[''].pkg_pn):
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500555 preferred = preferred_versions[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500556 latest = latest_versions[p]
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500557 requiredstr = ""
558 preferredstr = ""
559 if required[p]:
560 if preferred[0] is not None:
561 requiredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
562 else:
563 bb.fatal("REQUIRED_VERSION of package %s not available" % p)
564 else:
565 preferredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500567 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
568
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500569 if preferred == latest:
570 preferredstr = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500571
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500572 logger.plain("%-35s %25s %25s %25s", p, lateststr, preferredstr, requiredstr)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573
574 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
575 """
576 Show the outer or per-recipe environment
577 """
578 fn = None
579 envdata = None
Brad Bishop15ae2502019-06-18 21:44:24 -0400580 mc = ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500581 if not pkgs_to_build:
582 pkgs_to_build = []
583
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500584 orig_tracking = self.configuration.tracking
585 if not orig_tracking:
586 self.enableDataTracking()
587 self.reset()
Andrew Geissler9aee5002022-03-30 16:27:02 +0000588 # reset() resets to the UI requested value so we have to redo this
589 self.enableDataTracking()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500590
Brad Bishop15ae2502019-06-18 21:44:24 -0400591 def mc_base(p):
592 if p.startswith('mc:'):
593 s = p.split(':')
594 if len(s) == 2:
595 return s[1]
596 return None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500597
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500598 if buildfile:
599 # Parse the configuration here. We need to do it explicitly here since
600 # this showEnvironment() code path doesn't use the cache
601 self.parseConfiguration()
602
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600603 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500604 fn = self.matchFile(fn, mc)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600605 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500606 elif len(pkgs_to_build) == 1:
Brad Bishop15ae2502019-06-18 21:44:24 -0400607 mc = mc_base(pkgs_to_build[0])
608 if not mc:
609 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
610 if pkgs_to_build[0] in set(ignore.split()):
611 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500612
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000613 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.halt, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500614
Brad Bishop15ae2502019-06-18 21:44:24 -0400615 mc = runlist[0][0]
616 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500617
618 if fn:
619 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -0500620 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
621 envdata = bb_caches[mc].loadDataFull(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500622 except Exception as e:
623 parselog.exception("Unable to read %s", fn)
624 raise
Brad Bishop15ae2502019-06-18 21:44:24 -0400625 else:
626 if not mc in self.databuilder.mcdata:
627 bb.fatal('Not multiconfig named "%s" found' % mc)
628 envdata = self.databuilder.mcdata[mc]
629 data.expandKeys(envdata)
630 parse.ast.runAnonFuncs(envdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631
632 # Display history
633 with closing(StringIO()) as env:
634 self.data.inchistory.emit(env)
635 logger.plain(env.getvalue())
636
637 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500638 with closing(StringIO()) as env:
639 data.emit_env(env, envdata, True)
640 logger.plain(env.getvalue())
641
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000642 # emit the metadata which isn't valid shell
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500643 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600644 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500645 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500646
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500647 if not orig_tracking:
648 self.disableDataTracking()
649 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500650
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000651 def buildTaskData(self, pkgs_to_build, task, halt, allowincomplete=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500652 """
653 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
654 """
655 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
656
657 # A task of None means use the default task
658 if task is None:
659 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500660 if not task.startswith("do_"):
661 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500662
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500663 targetlist = self.checkPackages(pkgs_to_build, task)
664 fulltargetlist = []
665 defaulttask_implicit = ''
666 defaulttask_explicit = False
667 wildcard = False
668
669 # Wild card expansion:
Brad Bishop15ae2502019-06-18 21:44:24 -0400670 # Replace string such as "mc:*:bash"
671 # into "mc:A:bash mc:B:bash bash"
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500672 for k in targetlist:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600673 if k.startswith("mc:") and k.count(':') >= 2:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500674 if wildcard:
675 bb.fatal('multiconfig conflict')
676 if k.split(":")[1] == "*":
677 wildcard = True
678 for mc in self.multiconfigs:
679 if mc:
680 fulltargetlist.append(k.replace('*', mc))
681 # implicit default task
682 else:
683 defaulttask_implicit = k.split(":")[2]
684 else:
685 fulltargetlist.append(k)
686 else:
687 defaulttask_explicit = True
688 fulltargetlist.append(k)
689
690 if not defaulttask_explicit and defaulttask_implicit != '':
691 fulltargetlist.append(defaulttask_implicit)
692
693 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600694 taskdata = {}
695 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500696
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600697 for mc in self.multiconfigs:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000698 taskdata[mc] = bb.taskdata.TaskData(halt, skiplist=self.skiplist, allowincomplete=allowincomplete)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600699 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600700 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500701
702 current = 0
703 runlist = []
704 for k in fulltargetlist:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500705 origk = k
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600706 mc = ""
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600707 if k.startswith("mc:") and k.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600708 mc = k.split(":")[1]
709 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710 ktask = task
711 if ":do_" in k:
712 k2 = k.split(":do_")
713 k = k2[0]
714 ktask = k2[1]
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500715
716 if mc not in self.multiconfigs:
717 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named %s" % (origk, mc))
718
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600719 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500720 current += 1
721 if not ktask.startswith("do_"):
722 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
724 # e.g. in ASSUME_PROVIDED
725 continue
726 fn = taskdata[mc].build_targets[k][0]
727 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500728 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600729
Brad Bishop15ae2502019-06-18 21:44:24 -0400730 havemc = False
731 for mc in self.multiconfigs:
732 if taskdata[mc].get_mcdepends():
733 havemc = True
Brad Bishopf058f492019-01-28 23:50:33 -0500734
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800735 # No need to do check providers if there are no mcdeps or not an mc build
Brad Bishop15ae2502019-06-18 21:44:24 -0400736 if havemc or len(self.multiconfigs) > 1:
Andrew Geissler99467da2019-02-25 18:54:23 -0600737 seen = set()
738 new = True
739 # Make sure we can provide the multiconfig dependency
740 while new:
741 mcdeps = set()
742 # Add unresolved first, so we can get multiconfig indirect dependencies on time
743 for mc in self.multiconfigs:
744 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
745 mcdeps |= set(taskdata[mc].get_mcdepends())
746 new = False
747 for mc in self.multiconfigs:
748 for k in mcdeps:
749 if k in seen:
750 continue
751 l = k.split(':')
752 depmc = l[2]
753 if depmc not in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500754 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named configuration %s" % (k,depmc))
Andrew Geissler99467da2019-02-25 18:54:23 -0600755 else:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600756 logger.debug("Adding providers for multiconfig dependency %s" % l[3])
Andrew Geissler99467da2019-02-25 18:54:23 -0600757 taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
758 seen.add(k)
759 new = True
Brad Bishopf058f492019-01-28 23:50:33 -0500760
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600761 for mc in self.multiconfigs:
762 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
763
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500764 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600765 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500766
767 def prepareTreeData(self, pkgs_to_build, task):
768 """
769 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
770 """
771
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000772 # We set halt to False here to prevent unbuildable targets raising
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500773 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600774 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775
776 return runlist, taskdata
777
778 ######## WARNING : this function requires cache_extra to be enabled ########
779
780 def generateTaskDepTreeData(self, pkgs_to_build, task):
781 """
782 Create a dependency graph of pkgs_to_build including reverse dependency
783 information.
784 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500785 if not task.startswith("do_"):
786 task = "do_%s" % task
787
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500788 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600789 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500790 rq.rqdata.prepare()
791 return self.buildDependTree(rq, taskdata)
792
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600793 @staticmethod
794 def add_mc_prefix(mc, pn):
795 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -0400796 return "mc:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600797 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500798
799 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600800 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500801 depend_tree = {}
802 depend_tree["depends"] = {}
803 depend_tree["tdepends"] = {}
804 depend_tree["pn"] = {}
805 depend_tree["rdepends-pn"] = {}
806 depend_tree["packages"] = {}
807 depend_tree["rdepends-pkg"] = {}
808 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500809 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600810 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500811
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600812 for mc in taskdata:
813 for name, fn in list(taskdata[mc].get_providermap().items()):
814 pn = self.recipecaches[mc].pkg_fn[fn]
815 pn = self.add_mc_prefix(mc, pn)
816 if name != pn:
817 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
818 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500819
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600820 for tid in rq.rqdata.runtaskentries:
821 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
822 pn = self.recipecaches[mc].pkg_fn[taskfn]
823 pn = self.add_mc_prefix(mc, pn)
824 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500825 if pn not in depend_tree["pn"]:
826 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600827 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500828 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600829 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500830
831 # if we have extra caches, list all attributes they bring in
832 extra_info = []
833 for cache_class in self.caches_array:
834 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
835 cachefields = getattr(cache_class, 'cachefields', [])
836 extra_info = extra_info + cachefields
837
838 # for all attributes stored, add them to the dependency tree
839 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600840 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500841
842
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500843 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
844 if not dotname in depend_tree["tdepends"]:
845 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600846 for dep in rq.rqdata.runtaskentries[tid].depends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800847 (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
848 deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
Andrew Geissler595f6302022-01-24 19:11:47 +0000849 if depmc:
850 depmc = "mc:" + depmc + ":"
851 depend_tree["tdepends"][dotname].append("%s%s.%s" % (depmc, deppn, bb.runqueue.taskname_from_tid(dep)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600852 if taskfn not in seen_fns:
853 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500854 packages = []
855
856 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600857 for dep in taskdata[mc].depids[taskfn]:
858 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500859
860 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600861 for rdep in taskdata[mc].rdepids[taskfn]:
862 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500863
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600864 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500865 for package in rdepends:
866 depend_tree["rdepends-pkg"][package] = []
867 for rdepend in rdepends[package]:
868 depend_tree["rdepends-pkg"][package].append(rdepend)
869 packages.append(package)
870
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600871 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500872 for package in rrecs:
873 depend_tree["rrecs-pkg"][package] = []
874 for rdepend in rrecs[package]:
875 depend_tree["rrecs-pkg"][package].append(rdepend)
876 if not package in packages:
877 packages.append(package)
878
879 for package in packages:
880 if package not in depend_tree["packages"]:
881 depend_tree["packages"][package] = {}
882 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600883 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500884 depend_tree["packages"][package]["version"] = version
885
886 return depend_tree
887
888 ######## WARNING : this function requires cache_extra to be enabled ########
889 def generatePkgDepTreeData(self, pkgs_to_build, task):
890 """
891 Create a dependency tree of pkgs_to_build, returning the data.
892 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500893 if not task.startswith("do_"):
894 task = "do_%s" % task
895
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500896 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500897
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600898 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500899 depend_tree = {}
900 depend_tree["depends"] = {}
901 depend_tree["pn"] = {}
902 depend_tree["rdepends-pn"] = {}
903 depend_tree["rdepends-pkg"] = {}
904 depend_tree["rrecs-pkg"] = {}
905
906 # if we have extra caches, list all attributes they bring in
907 extra_info = []
908 for cache_class in self.caches_array:
909 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
910 cachefields = getattr(cache_class, 'cachefields', [])
911 extra_info = extra_info + cachefields
912
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600913 tids = []
914 for mc in taskdata:
915 for tid in taskdata[mc].taskentries:
916 tids.append(tid)
917
918 for tid in tids:
919 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
920
921 pn = self.recipecaches[mc].pkg_fn[taskfn]
922 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500923
924 if pn not in depend_tree["pn"]:
925 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600926 depend_tree["pn"][pn]["filename"] = taskfn
927 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500928 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600929 rdepends = self.recipecaches[mc].rundeps[taskfn]
930 rrecs = self.recipecaches[mc].runrecs[taskfn]
931 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500932
933 # for all extra attributes stored, add them to the dependency tree
934 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600935 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500936
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600937 if taskfn not in seen_fns:
938 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500939
940 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500941 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500942 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600943 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
944 fn_provider = taskdata[mc].build_targets[dep][0]
945 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500946 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500947 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600948 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500949 depend_tree["depends"][pn].append(pn_provider)
950
951 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600952 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500953 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600954 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
955 fn_rprovider = taskdata[mc].run_targets[rdep][0]
956 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500957 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600958 pn_rprovider = rdep
959 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500960 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
961
962 depend_tree["rdepends-pkg"].update(rdepends)
963 depend_tree["rrecs-pkg"].update(rrecs)
964
965 return depend_tree
966
967 def generateDepTreeEvent(self, pkgs_to_build, task):
968 """
969 Create a task dependency graph of pkgs_to_build.
970 Generate an event with the result
971 """
972 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
973 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
974
975 def generateDotGraphFiles(self, pkgs_to_build, task):
976 """
977 Create a task dependency graph of pkgs_to_build.
978 Save the result to a set of .dot files.
979 """
980
981 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
982
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500983 with open('pn-buildlist', 'w') as f:
984 for pn in depgraph["pn"]:
985 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500986 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500987
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500988 # Remove old format output files to ensure no confusion with stale data
989 try:
990 os.unlink('pn-depends.dot')
991 except FileNotFoundError:
992 pass
993 try:
994 os.unlink('package-depends.dot')
995 except FileNotFoundError:
996 pass
Brad Bishop79641f22019-09-10 07:20:22 -0400997 try:
998 os.unlink('recipe-depends.dot')
999 except FileNotFoundError:
1000 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001001
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001002 with open('task-depends.dot', 'w') as f:
1003 f.write("digraph depends {\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -04001004 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001005 (pn, taskname) = task.rsplit(".", 1)
1006 fn = depgraph["pn"][pn]["filename"]
1007 version = depgraph["pn"][pn]["version"]
1008 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 -04001009 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001010 f.write('"%s" -> "%s"\n' % (task, dep))
1011 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001012 logger.info("Task dependencies saved to 'task-depends.dot'")
1013
1014 def show_appends_with_no_recipes(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001015 appends_without_recipes = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001016 # Determine which bbappends haven't been applied
Andrew Geissler5a43b432020-06-13 10:46:56 -05001017 for mc in self.multiconfigs:
1018 # First get list of recipes, including skipped
1019 recipefns = list(self.recipecaches[mc].pkg_fn.keys())
1020 recipefns.extend(self.skiplist.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001021
Andrew Geissler5a43b432020-06-13 10:46:56 -05001022 # Work out list of bbappends that have been applied
1023 applied_appends = []
1024 for fn in recipefns:
1025 applied_appends.extend(self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001026
Andrew Geissler5a43b432020-06-13 10:46:56 -05001027 appends_without_recipes[mc] = []
1028 for _, appendfn in self.collections[mc].bbappends:
1029 if not appendfn in applied_appends:
1030 appends_without_recipes[mc].append(appendfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001031
Andrew Geissler5a43b432020-06-13 10:46:56 -05001032 msgs = []
1033 for mc in sorted(appends_without_recipes.keys()):
1034 if appends_without_recipes[mc]:
1035 msgs.append('No recipes in %s available for:\n %s' % (mc if mc else 'default',
1036 '\n '.join(appends_without_recipes[mc])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001037
Andrew Geissler5a43b432020-06-13 10:46:56 -05001038 if msgs:
1039 msg = "\n".join(msgs)
1040 warn_only = self.databuilder.mcdata[mc].getVar("BB_DANGLINGAPPENDS_WARNONLY", \
1041 False) or "no"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001042 if warn_only.lower() in ("1", "yes", "true"):
1043 bb.warn(msg)
1044 else:
1045 bb.fatal(msg)
1046
1047 def handlePrefProviders(self):
1048
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001049 for mc in self.multiconfigs:
1050 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001051 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001052
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001053 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001054 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001055 try:
1056 (providee, provider) = p.split(':')
1057 except:
1058 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
1059 continue
1060 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
1061 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
1062 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001063
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001064 def findConfigFilePath(self, configfile):
1065 """
1066 Find the location on disk of configfile and if it exists and was parsed by BitBake
1067 emit the ConfigFilePathFound event with the path to the file.
1068 """
1069 path = bb.cookerdata.findConfigFile(configfile, self.data)
1070 if not path:
1071 return
1072
1073 # Generate a list of parsed configuration files by searching the files
1074 # listed in the __depends and __base_depends variables with a .conf suffix.
1075 conffiles = []
1076 dep_files = self.data.getVar('__base_depends', False) or []
1077 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
1078
1079 for f in dep_files:
1080 if f[0].endswith(".conf"):
1081 conffiles.append(f[0])
1082
1083 _, conf, conffile = path.rpartition("conf/")
1084 match = os.path.join(conf, conffile)
1085 # Try and find matches for conf/conffilename.conf as we don't always
1086 # have the full path to the file.
1087 for cfg in conffiles:
1088 if cfg.endswith(match):
1089 bb.event.fire(bb.event.ConfigFilePathFound(path),
1090 self.data)
1091 break
1092
1093 def findFilesMatchingInDir(self, filepattern, directory):
1094 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001095 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001096 'directory' in each BBPATH. i.e. to find all rootfs package classes available
1097 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
1098 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001099 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001100 """
1101
1102 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001103 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001104 for path in bbpaths:
1105 dirpath = os.path.join(path, directory)
1106 if os.path.exists(dirpath):
1107 for root, dirs, files in os.walk(dirpath):
1108 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001109 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001110 matches.append(f)
1111
1112 if matches:
1113 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1114
Patrick Williams93c203f2021-10-06 16:15:23 -05001115 def testCookerCommandEvent(self, filepattern):
1116 # Dummy command used by OEQA selftest to test tinfoil without IO
1117 matches = ["A", "B"]
1118 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1119
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001120 def findProviders(self, mc=''):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001121 return bb.providers.findProviders(self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001122
1123 def findBestProvider(self, pn, mc=''):
1124 if pn in self.recipecaches[mc].providers:
1125 filenames = self.recipecaches[mc].providers[pn]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001126 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.databuilder.mcdata[mc], self.recipecaches[mc])
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001127 if eligible is not None:
1128 filename = eligible[0]
1129 else:
1130 filename = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001131 return None, None, None, filename
1132 elif pn in self.recipecaches[mc].pkg_pn:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001133 (latest, latest_f, preferred_ver, preferred_file, required) = bb.providers.findBestProvider(pn, self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
1134 if required and preferred_file is None:
1135 return None, None, None, None
1136 return (latest, latest_f, preferred_ver, preferred_file)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001137 else:
1138 return None, None, None, None
1139
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001140 def findConfigFiles(self, varname):
1141 """
1142 Find config files which are appropriate values for varname.
1143 i.e. MACHINE, DISTRO
1144 """
1145 possible = []
1146 var = varname.lower()
1147
1148 data = self.data
1149 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001150 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001151 for path in bbpaths:
1152 confpath = os.path.join(path, "conf", var)
1153 if os.path.exists(confpath):
1154 for root, dirs, files in os.walk(confpath):
1155 # get all child files, these are appropriate values
1156 for f in files:
1157 val, sep, end = f.rpartition('.')
1158 if end == 'conf':
1159 possible.append(val)
1160
1161 if possible:
1162 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1163
1164 def findInheritsClass(self, klass):
1165 """
1166 Find all recipes which inherit the specified class
1167 """
1168 pkg_list = []
1169
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001170 for pfn in self.recipecaches[''].pkg_fn:
1171 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001172 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001173 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001174
1175 return pkg_list
1176
1177 def generateTargetsTree(self, klass=None, pkgs=None):
1178 """
1179 Generate a dependency tree of buildable targets
1180 Generate an event with the result
1181 """
1182 # if the caller hasn't specified a pkgs list default to universe
1183 if not pkgs:
1184 pkgs = ['universe']
1185 # if inherited_class passed ensure all recipes which inherit the
1186 # specified class are included in pkgs
1187 if klass:
1188 extra_pkgs = self.findInheritsClass(klass)
1189 pkgs = pkgs + extra_pkgs
1190
1191 # generate a dependency tree for all our packages
1192 tree = self.generatePkgDepTreeData(pkgs, 'build')
1193 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1194
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001195 def interactiveMode( self ):
1196 """Drop off into a shell"""
1197 try:
1198 from bb import shell
1199 except ImportError:
1200 parselog.exception("Interactive mode not available")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001201 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001202 else:
1203 shell.start( self )
1204
1205
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001206 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001207 """Handle collections"""
1208 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001209 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001210 if collections:
1211 collection_priorities = {}
1212 collection_depends = {}
1213 collection_list = collections.split()
1214 min_prio = 0
1215 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001216 bb.debug(1,'Processing %s in collection list' % (c))
1217
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001218 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001219 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001220 if priority:
1221 try:
1222 prio = int(priority)
1223 except ValueError:
1224 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1225 errors = True
1226 if min_prio == 0 or prio < min_prio:
1227 min_prio = prio
1228 collection_priorities[c] = prio
1229 else:
1230 collection_priorities[c] = None
1231
1232 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001233 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001234 if deps:
1235 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001236 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001237 except bb.utils.VersionStringException as vse:
1238 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001239 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001240 if dep in collection_list:
1241 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001242 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001243 (op, depver) = opstr.split()
1244 if layerver:
1245 try:
1246 res = bb.utils.vercmp_string_op(layerver, depver, op)
1247 except bb.utils.VersionStringException as vse:
1248 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1249 if not res:
1250 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)
1251 errors = True
1252 else:
1253 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)
1254 errors = True
1255 else:
1256 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1257 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001258 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001259 else:
1260 collection_depends[c] = []
1261
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001262 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001263 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001264 if recs:
1265 try:
1266 recDict = bb.utils.explode_dep_versions2(recs)
1267 except bb.utils.VersionStringException as vse:
1268 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1269 for rec, oplist in list(recDict.items()):
1270 if rec in collection_list:
1271 if oplist:
1272 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001273 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001274 if layerver:
1275 (op, recver) = opstr.split()
1276 try:
1277 res = bb.utils.vercmp_string_op(layerver, recver, op)
1278 except bb.utils.VersionStringException as vse:
1279 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1280 if not res:
1281 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)
1282 continue
1283 else:
1284 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)
1285 continue
1286 parselog.debug(3,"Layer '%s' recommends layer '%s', so we are adding it", c, rec)
1287 collection_depends[c].append(rec)
1288 else:
1289 parselog.debug(3,"Layer '%s' recommends layer '%s', but this layer is not enabled in your configuration", c, rec)
1290
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001291 # Recursively work out collection priorities based on dependencies
1292 def calc_layer_priority(collection):
1293 if not collection_priorities[collection]:
1294 max_depprio = min_prio
1295 for dep in collection_depends[collection]:
1296 calc_layer_priority(dep)
1297 depprio = collection_priorities[dep]
1298 if depprio > max_depprio:
1299 max_depprio = depprio
1300 max_depprio += 1
1301 parselog.debug(1, "Calculated priority of layer %s as %d", collection, max_depprio)
1302 collection_priorities[collection] = max_depprio
1303
1304 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1305 for c in collection_list:
1306 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001307 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001308 if regex is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001309 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1310 errors = True
1311 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001312 elif regex == "":
1313 parselog.debug(1, "BBFILE_PATTERN_%s is empty" % c)
Brad Bishop19323692019-04-05 15:28:33 -04001314 cre = re.compile('^NULL$')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001315 errors = False
1316 else:
1317 try:
1318 cre = re.compile(regex)
1319 except re.error:
1320 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1321 errors = True
1322 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001323 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001324 if errors:
1325 # We've already printed the actual error(s)
1326 raise CollectionError("Errors during parsing layer configuration")
1327
1328 def buildSetVars(self):
1329 """
1330 Setup any variables needed before starting a build
1331 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001332 t = time.gmtime()
1333 for mc in self.databuilder.mcdata:
1334 ds = self.databuilder.mcdata[mc]
1335 if not ds.getVar("BUILDNAME", False):
1336 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1337 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1338 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1339 ds.setVar("TIME", time.strftime('%H%M%S', t))
1340
1341 def reset_mtime_caches(self):
1342 """
1343 Reset mtime caches - this is particularly important when memory resident as something
1344 which is cached is not unlikely to have changed since the last invocation (e.g. a
1345 file associated with a recipe might have been modified by the user).
1346 """
1347 build.reset_cache()
1348 bb.fetch._checksum_cache.mtime_cache.clear()
1349 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1350 if siggen_cache:
1351 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001352
Andrew Geissler5a43b432020-06-13 10:46:56 -05001353 def matchFiles(self, bf, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001354 """
1355 Find the .bb files which match the expression in 'buildfile'.
1356 """
1357 if bf.startswith("/") or bf.startswith("../"):
1358 bf = os.path.abspath(bf)
1359
Andrew Geissler5a43b432020-06-13 10:46:56 -05001360 self.collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)}
1361 filelist, masked, searchdirs = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001362 try:
1363 os.stat(bf)
1364 bf = os.path.abspath(bf)
1365 return [bf]
1366 except OSError:
1367 regexp = re.compile(bf)
1368 matches = []
1369 for f in filelist:
1370 if regexp.search(f) and os.path.isfile(f):
1371 matches.append(f)
1372 return matches
1373
Andrew Geissler5a43b432020-06-13 10:46:56 -05001374 def matchFile(self, buildfile, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001375 """
1376 Find the .bb file which matches the expression in 'buildfile'.
1377 Raise an error if multiple files
1378 """
Andrew Geissler5a43b432020-06-13 10:46:56 -05001379 matches = self.matchFiles(buildfile, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001380 if len(matches) != 1:
1381 if matches:
1382 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1383 if matches:
1384 for f in matches:
1385 msg += "\n %s" % f
1386 parselog.error(msg)
1387 else:
1388 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1389 raise NoSpecificMatch
1390 return matches[0]
1391
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001392 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001393 """
1394 Build the file matching regexp buildfile
1395 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001396 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001397
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001398 # Too many people use -b because they think it's how you normally
1399 # specify a target to be built, so show a warning
1400 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1401
1402 self.buildFileInternal(buildfile, task)
1403
1404 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1405 """
1406 Build the file matching regexp buildfile
1407 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001408
1409 # Parse the configuration here. We need to do it explicitly here since
1410 # buildFile() doesn't use the cache
1411 self.parseConfiguration()
1412
1413 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001414 if task is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001415 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001416 if not task.startswith("do_"):
1417 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001418
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001419 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001420 fn = self.matchFile(fn, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001421
1422 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001423 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001424
Andrew Geissler5a43b432020-06-13 10:46:56 -05001425 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001426
Andrew Geissler5a43b432020-06-13 10:46:56 -05001427 infos = bb_caches[mc].parse(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001428 infos = dict(infos)
1429
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001430 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001431 try:
1432 info_array = infos[fn]
1433 except KeyError:
1434 bb.fatal("%s does not exist" % fn)
1435
1436 if info_array[0].skipped:
1437 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1438
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001439 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001440
1441 # Tweak some variables
1442 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001443 self.recipecaches[mc].ignored_dependencies = set()
1444 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001445 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001446
1447 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001448 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1449 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001450 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1451 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001452
1453 # Invalidate task for target if force mode active
1454 if self.configuration.force:
1455 logger.verbose("Invalidate task %s, %s", task, fn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001456 bb.parse.siggen.invalidate_task(task, self.recipecaches[mc], fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001457
1458 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001459 taskdata = {}
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001460 taskdata[mc] = bb.taskdata.TaskData(self.configuration.halt)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001461 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001462
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001463 if quietlog:
1464 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1465 bb.runqueue.logger.setLevel(logging.WARNING)
1466
1467 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1468 if fireevents:
1469 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001470
1471 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001472 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001473
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001474 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001475
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001476 def buildFileIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001477
1478 msg = None
1479 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001480 if halt or self.state == state.forceshutdown:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001481 rq.finish_runqueue(True)
1482 msg = "Forced shutdown"
1483 interrupted = 2
1484 elif self.state == state.shutdown:
1485 rq.finish_runqueue(False)
1486 msg = "Stopped build"
1487 interrupted = 1
1488 failures = 0
1489 try:
1490 retval = rq.execute_runqueue()
1491 except runqueue.TaskFailure as exc:
1492 failures += len(exc.args)
1493 retval = False
1494 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001495 self.command.finishAsyncCommand(str(exc))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001496 if quietlog:
1497 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001498 return False
1499
1500 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001501 if fireevents:
1502 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 -05001503 self.command.finishAsyncCommand(msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001504 # We trashed self.recipecaches above
1505 self.parsecache_valid = False
1506 self.configuration.limited_deps = False
1507 bb.parse.siggen.reset(self.data)
1508 if quietlog:
1509 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001510 return False
1511 if retval is True:
1512 return True
1513 return retval
1514
Andrew Geissler635e0e42020-08-21 15:58:33 -05001515 self.idleCallBackRegister(buildFileIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001516
1517 def buildTargets(self, targets, task):
1518 """
1519 Attempt to build the targets specified
1520 """
1521
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001522 def buildTargetsIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001523 msg = None
1524 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001525 if halt or self.state == state.forceshutdown:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001526 rq.finish_runqueue(True)
1527 msg = "Forced shutdown"
1528 interrupted = 2
1529 elif self.state == state.shutdown:
1530 rq.finish_runqueue(False)
1531 msg = "Stopped build"
1532 interrupted = 1
1533 failures = 0
1534 try:
1535 retval = rq.execute_runqueue()
1536 except runqueue.TaskFailure as exc:
1537 failures += len(exc.args)
1538 retval = False
1539 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001540 self.command.finishAsyncCommand(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001541 return False
1542
1543 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001544 try:
1545 for mc in self.multiconfigs:
1546 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1547 finally:
1548 self.command.finishAsyncCommand(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001549 return False
1550 if retval is True:
1551 return True
1552 return retval
1553
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001554 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001555 self.buildSetVars()
1556
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001557 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001558 if task is None:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001559 task = self.configuration.cmd
1560
1561 if not task.startswith("do_"):
1562 task = "do_%s" % task
1563
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001564 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1565
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001566 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001567
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001568 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.halt)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001569
1570 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001571
1572 # make targets to always look as <target>:do_<task>
1573 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001574 for target in runlist:
1575 if target[0]:
Brad Bishop15ae2502019-06-18 21:44:24 -04001576 ntargets.append("mc:%s:%s:%s" % (target[0], target[1], target[2]))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001577 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001578
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001579 for mc in self.multiconfigs:
1580 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001581
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001582 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001583 if 'universe' in targets:
1584 rq.rqdata.warn_multi_bb = True
1585
Andrew Geissler635e0e42020-08-21 15:58:33 -05001586 self.idleCallBackRegister(buildTargetsIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001587
1588
1589 def getAllKeysWithFlags(self, flaglist):
1590 dump = {}
1591 for k in self.data.keys():
1592 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001593 expand = True
1594 flags = self.data.getVarFlags(k)
1595 if flags and "func" in flags and "python" in flags:
1596 expand = False
1597 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001598 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1599 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001600 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001601 'history' : self.data.varhistory.variable(k),
1602 }
1603 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001604 if flags and d in flags:
1605 dump[k][d] = flags[d]
1606 else:
1607 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001608 except Exception as e:
1609 print(e)
1610 return dump
1611
1612
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001613 def updateCacheSync(self):
1614 if self.state == state.running:
1615 return
1616
1617 # reload files for which we got notifications
1618 for p in self.inotify_modified_files:
1619 bb.parse.update_cache(p)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001620 if p in bb.parse.BBHandler.cached_statements:
1621 del bb.parse.BBHandler.cached_statements[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001622 self.inotify_modified_files = []
1623
1624 if not self.baseconfig_valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001625 logger.debug("Reloading base configuration data")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001626 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001627 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001628
1629 # This is called for all async commands when self.state != running
1630 def updateCache(self):
1631 if self.state == state.running:
1632 return
1633
1634 if self.state in (state.shutdown, state.forceshutdown, state.error):
1635 if hasattr(self.parser, 'shutdown'):
Andrew Geissler9aee5002022-03-30 16:27:02 +00001636 self.parser.shutdown(clean=False)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001637 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001638 raise bb.BBHandledException()
1639
1640 if self.state != state.parsing:
1641 self.updateCacheSync()
1642
1643 if self.state != state.parsing and not self.parsecache_valid:
Patrick Williamsde0582f2022-04-08 10:23:27 -05001644 self.setupParserWatcher()
1645
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001646 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001647 self.parseConfiguration ()
1648 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001649 for mc in self.multiconfigs:
1650 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001651
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001652 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001653 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001654 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001655
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001656 for dep in self.configuration.extra_assume_provided:
1657 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001658
Andrew Geissler5a43b432020-06-13 10:46:56 -05001659 self.collections = {}
1660
1661 mcfilelist = {}
1662 total_masked = 0
1663 searchdirs = set()
1664 for mc in self.multiconfigs:
1665 self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc)
1666 (filelist, masked, search) = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
1667
1668 mcfilelist[mc] = filelist
1669 total_masked += masked
1670 searchdirs |= set(search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001671
1672 # Add inotify watches for directories searched for bb/bbappend files
1673 for dirent in searchdirs:
1674 self.add_filewatch([[dirent]], dirs=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001675
Andrew Geissler5a43b432020-06-13 10:46:56 -05001676 self.parser = CookerParser(self, mcfilelist, total_masked)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001677 self.parsecache_valid = True
1678
1679 self.state = state.parsing
1680
1681 if not self.parser.parse_next():
1682 collectlog.debug(1, "parsing complete")
1683 if self.parser.error:
1684 raise bb.BBHandledException()
1685 self.show_appends_with_no_recipes()
1686 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001687 for mc in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001688 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 -05001689 self.state = state.running
1690
1691 # Send an event listing all stamps reachable after parsing
1692 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001693 for mc in self.multiconfigs:
1694 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1695 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001696 return None
1697
1698 return True
1699
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001700 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001701
1702 # Return a copy, don't modify the original
1703 pkgs_to_build = pkgs_to_build[:]
1704
Andrew Geissler595f6302022-01-24 19:11:47 +00001705 if not pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001706 raise NothingToBuild
1707
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001708 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001709 for pkg in pkgs_to_build.copy():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001710 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001711 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Brad Bishop15ae2502019-06-18 21:44:24 -04001712 if pkg.startswith("multiconfig:"):
1713 pkgs_to_build.remove(pkg)
1714 pkgs_to_build.append(pkg.replace("multiconfig:", "mc:"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001715
1716 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001717 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001718 for mc in self.multiconfigs:
1719 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1720 for t in self.recipecaches[mc].world_target:
1721 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001722 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001723 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001724
1725 if 'universe' in pkgs_to_build:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001726 parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001727 parselog.debug(1, "collating packages for \"universe\"")
1728 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001729 for mc in self.multiconfigs:
1730 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001731 if task:
1732 foundtask = False
1733 for provider_fn in self.recipecaches[mc].providers[t]:
1734 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1735 foundtask = True
1736 break
1737 if not foundtask:
1738 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1739 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001740 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001741 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001742 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001743
1744 return pkgs_to_build
1745
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001746 def pre_serve(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001747 return
1748
1749 def post_serve(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001750 self.shutdown(force=True)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001751 prserv.serv.auto_shutdown()
Patrick Williams45852732022-04-02 08:58:32 -05001752 if hasattr(bb.parse, "siggen"):
1753 bb.parse.siggen.exit()
Brad Bishop08902b02019-08-20 09:16:51 -04001754 if self.hashserv:
1755 self.hashserv.process.terminate()
1756 self.hashserv.process.join()
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001757 if hasattr(self, "data"):
1758 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001759
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001760 def shutdown(self, force = False):
1761 if force:
1762 self.state = state.forceshutdown
1763 else:
1764 self.state = state.shutdown
1765
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001766 if self.parser:
Andrew Geissler9aee5002022-03-30 16:27:02 +00001767 self.parser.shutdown(clean=not force)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001768 self.parser.final_cleanup()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001769
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001770 def finishcommand(self):
1771 self.state = state.initial
1772
1773 def reset(self):
Patrick Williams45852732022-04-02 08:58:32 -05001774 if hasattr(bb.parse, "siggen"):
1775 bb.parse.siggen.exit()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001776 self.initConfigurationData()
Brad Bishop08902b02019-08-20 09:16:51 -04001777 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001778
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001779 def clientComplete(self):
1780 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001781 self.finishcommand()
1782 self.extraconfigdata = {}
1783 self.command.reset()
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001784 if hasattr(self, "data"):
1785 self.databuilder.reset()
1786 self.data = self.databuilder.data
Andrew Geissler82c905d2020-04-13 13:39:40 -05001787 self.parsecache_valid = False
1788 self.baseconfig_valid = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001789
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001790
1791class CookerExit(bb.event.Event):
1792 """
1793 Notify clients of the Cooker shutdown
1794 """
1795
1796 def __init__(self):
1797 bb.event.Event.__init__(self)
1798
1799
1800class CookerCollectFiles(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001801 def __init__(self, priorities, mc=''):
1802 self.mc = mc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001803 self.bbappends = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001804 # Priorities is a list of tuples, with the second element as the pattern.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001805 # We need to sort the list with the longest pattern first, and so on to
1806 # the shortest. This allows nested layers to be properly evaluated.
1807 self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001808
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001809 def calc_bbfile_priority(self, filename):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001810 for _, _, regex, pri in self.bbfile_config_priorities:
1811 if regex.match(filename):
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001812 return pri, regex
1813 return 0, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001814
1815 def get_bbfiles(self):
1816 """Get list of default .bb files by reading out the current directory"""
1817 path = os.getcwd()
1818 contents = os.listdir(path)
1819 bbfiles = []
1820 for f in contents:
1821 if f.endswith(".bb"):
1822 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1823 return bbfiles
1824
1825 def find_bbfiles(self, path):
1826 """Find all the .bb and .bbappend files in a directory"""
1827 found = []
1828 for dir, dirs, files in os.walk(path):
1829 for ignored in ('SCCS', 'CVS', '.svn'):
1830 if ignored in dirs:
1831 dirs.remove(ignored)
1832 found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
1833
1834 return found
1835
1836 def collect_bbfiles(self, config, eventdata):
1837 """Collect all available .bb build files"""
1838 masked = 0
1839
1840 collectlog.debug(1, "collecting .bb files")
1841
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001842 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001843
1844 # Sort files by priority
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001845 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem)[0] )
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001846 config.setVar("BBFILES_PRIORITIZED", " ".join(files))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001847
Andrew Geissler595f6302022-01-24 19:11:47 +00001848 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001849 files = self.get_bbfiles()
1850
Andrew Geissler595f6302022-01-24 19:11:47 +00001851 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001852 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1853 bb.event.fire(CookerExit(), eventdata)
1854
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001855 # We need to track where we look so that we can add inotify watches. There
1856 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001857 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001858 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001859 if hasattr(os, 'scandir'):
1860 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001861 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001862
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001863 def ourlistdir(d):
1864 searchdirs.append(d)
1865 return origlistdir(d)
1866
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001867 def ourscandir(d):
1868 searchdirs.append(d)
1869 return origscandir(d)
1870
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001871 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001872 if hasattr(os, 'scandir'):
1873 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001874 try:
1875 # Can't use set here as order is important
1876 newfiles = []
1877 for f in files:
1878 if os.path.isdir(f):
1879 dirfiles = self.find_bbfiles(f)
1880 for g in dirfiles:
1881 if g not in newfiles:
1882 newfiles.append(g)
1883 else:
1884 globbed = glob.glob(f)
1885 if not globbed and os.path.exists(f):
1886 globbed = [f]
1887 # glob gives files in order on disk. Sort to be deterministic.
1888 for g in sorted(globbed):
1889 if g not in newfiles:
1890 newfiles.append(g)
1891 finally:
1892 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001893 if hasattr(os, 'scandir'):
1894 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001895
1896 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001897
1898 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001899 # First validate the individual regular expressions and ignore any
1900 # that do not compile
1901 bbmasks = []
1902 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001903 # When constructing an older style single regex, it's possible for BBMASK
1904 # to end up beginning with '|', which matches and masks _everything_.
1905 if mask.startswith("|"):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001906 collectlog.warning("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001907 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001908 try:
1909 re.compile(mask)
1910 bbmasks.append(mask)
1911 except sre_constants.error:
1912 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1913
1914 # Then validate the combined regular expressions. This should never
1915 # fail, but better safe than sorry...
1916 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001917 try:
1918 bbmask_compiled = re.compile(bbmask)
1919 except sre_constants.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001920 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1921 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001922
1923 bbfiles = []
1924 bbappend = []
1925 for f in newfiles:
1926 if bbmask and bbmask_compiled.search(f):
1927 collectlog.debug(1, "skipping masked file %s", f)
1928 masked += 1
1929 continue
1930 if f.endswith('.bb'):
1931 bbfiles.append(f)
1932 elif f.endswith('.bbappend'):
1933 bbappend.append(f)
1934 else:
1935 collectlog.debug(1, "skipping %s: unknown file extension", f)
1936
1937 # Build a list of .bbappend files for each .bb file
1938 for f in bbappend:
1939 base = os.path.basename(f).replace('.bbappend', '.bb')
1940 self.bbappends.append((base, f))
1941
1942 # Find overlayed recipes
1943 # bbfiles will be in priority order which makes this easy
1944 bbfile_seen = dict()
1945 self.overlayed = defaultdict(list)
1946 for f in reversed(bbfiles):
1947 base = os.path.basename(f)
1948 if base not in bbfile_seen:
1949 bbfile_seen[base] = f
1950 else:
1951 topfile = bbfile_seen[base]
1952 self.overlayed[topfile].append(f)
1953
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001954 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001955
1956 def get_file_appends(self, fn):
1957 """
1958 Returns a list of .bbappend files to apply to fn
1959 """
1960 filelist = []
1961 f = os.path.basename(fn)
1962 for b in self.bbappends:
1963 (bbappend, filename) = b
1964 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1965 filelist.append(filename)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001966 return tuple(filelist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001967
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001968 def collection_priorities(self, pkgfns, fns, d):
1969 # Return the priorities of the entries in pkgfns
1970 # Also check that all the regexes in self.bbfile_config_priorities are used
1971 # (but to do that we need to ensure skipped recipes aren't counted, nor
1972 # collections in BBFILE_PATTERN_IGNORE_EMPTY)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001973
1974 priorities = {}
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001975 seen = set()
1976 matched = set()
1977
1978 matched_regex = set()
1979 unmatched_regex = set()
1980 for _, _, regex, _ in self.bbfile_config_priorities:
1981 unmatched_regex.add(regex)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001982
1983 # Calculate priorities for each file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001984 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001985 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001986 priorities[p], regex = self.calc_bbfile_priority(realfn)
1987 if regex in unmatched_regex:
1988 matched_regex.add(regex)
1989 unmatched_regex.remove(regex)
1990 seen.add(realfn)
1991 if regex:
1992 matched.add(realfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001993
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001994 if unmatched_regex:
1995 # Account for bbappend files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001996 for b in self.bbappends:
1997 (bbfile, append) = b
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001998 seen.add(append)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001999
Andrew Geisslerb7d28612020-07-24 16:15:54 -05002000 # Account for skipped recipes
2001 seen.update(fns)
2002
2003 seen.difference_update(matched)
2004
2005 def already_matched(fn):
2006 for regex in matched_regex:
2007 if regex.match(fn):
2008 return True
2009 return False
2010
2011 for unmatch in unmatched_regex.copy():
2012 for fn in seen:
2013 if unmatch.match(fn):
2014 # If the bbappend or file was already matched by another regex, skip it
2015 # e.g. for a layer within a layer, the outer regex could match, the inner
2016 # regex may match nothing and we should warn about that
2017 if already_matched(fn):
2018 continue
2019 unmatched_regex.remove(unmatch)
2020 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002021
2022 for collection, pattern, regex, _ in self.bbfile_config_priorities:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05002023 if regex in unmatched_regex:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002024 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Andrew Geissler5a43b432020-06-13 10:46:56 -05002025 collectlog.warning("No bb files in %s matched BBFILE_PATTERN_%s '%s'" % (self.mc if self.mc else 'default',
2026 collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002027
2028 return priorities
2029
2030class ParsingFailure(Exception):
2031 def __init__(self, realexception, recipe):
2032 self.realexception = realexception
2033 self.recipe = recipe
2034 Exception.__init__(self, realexception, recipe)
2035
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002036class Parser(multiprocessing.Process):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002037 def __init__(self, jobs, results, quit, profile):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002038 self.jobs = jobs
2039 self.results = results
2040 self.quit = quit
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002041 multiprocessing.Process.__init__(self)
2042 self.context = bb.utils.get_context().copy()
2043 self.handlers = bb.event.get_class_handlers().copy()
2044 self.profile = profile
Andrew Geissler9aee5002022-03-30 16:27:02 +00002045 self.queue_signals = False
2046 self.signal_received = []
2047 self.signal_threadlock = threading.Lock()
2048
2049 def catch_sig(self, signum, frame):
2050 if self.queue_signals:
2051 self.signal_received.append(signum)
2052 else:
2053 self.handle_sig(signum, frame)
2054
2055 def handle_sig(self, signum, frame):
2056 if signum == signal.SIGTERM:
2057 signal.signal(signal.SIGTERM, signal.SIG_DFL)
2058 os.kill(os.getpid(), signal.SIGTERM)
2059 elif signum == signal.SIGINT:
2060 signal.default_int_handler(signum, frame)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002061
2062 def run(self):
2063
2064 if not self.profile:
2065 self.realrun()
2066 return
2067
2068 try:
2069 import cProfile as profile
2070 except:
2071 import profile
2072 prof = profile.Profile()
2073 try:
2074 profile.Profile.runcall(prof, self.realrun)
2075 finally:
2076 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
2077 prof.dump_stats(logfile)
2078
2079 def realrun(self):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002080 # Signal handling here is hard. We must not terminate any process or thread holding the write
2081 # lock for the event stream as it will not be released, ever, and things will hang.
2082 # Python handles signals in the main thread/process but they can be raised from any thread and
2083 # we want to defer processing of any SIGTERM/SIGINT signal until we're outside the critical section
2084 # and don't hold the lock (see server/process.py). We therefore always catch the signals (so any
2085 # new thread should also do so) and we defer handling but we handle with the local thread lock
2086 # held (a threading lock, not a multiprocessing one) so that no other thread in the process
2087 # can be in the critical section.
2088 signal.signal(signal.SIGTERM, self.catch_sig)
2089 signal.signal(signal.SIGHUP, signal.SIG_DFL)
2090 signal.signal(signal.SIGINT, self.catch_sig)
2091 bb.utils.set_process_name(multiprocessing.current_process().name)
2092 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2093 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002094
2095 pending = []
Andrew Geissler9aee5002022-03-30 16:27:02 +00002096 try:
2097 while True:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002098 try:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002099 self.quit.get_nowait()
2100 except queue.Empty:
2101 pass
2102 else:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002103 break
Andrew Geissler9aee5002022-03-30 16:27:02 +00002104
2105 if pending:
2106 result = pending.pop()
2107 else:
2108 try:
2109 job = self.jobs.pop()
2110 except IndexError:
2111 break
2112 result = self.parse(*job)
2113 # Clear the siggen cache after parsing to control memory usage, its huge
2114 bb.parse.siggen.postparsing_clean_cache()
2115 try:
2116 self.results.put(result, timeout=0.25)
2117 except queue.Full:
2118 pending.append(result)
2119 finally:
2120 self.results.close()
2121 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002122
Andrew Geissler5a43b432020-06-13 10:46:56 -05002123 def parse(self, mc, cache, filename, appends):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002124 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002125 origfilter = bb.event.LogHandler.filter
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002126 # Record the filename we're parsing into any events generated
2127 def parse_filter(self, record):
2128 record.taskpid = bb.event.worker_pid
2129 record.fn = filename
2130 return True
2131
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002132 # Reset our environment and handlers to the original settings
2133 bb.utils.set_context(self.context.copy())
2134 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002135 bb.event.LogHandler.filter = parse_filter
2136
Andrew Geissler5a43b432020-06-13 10:46:56 -05002137 return True, mc, cache.parse(filename, appends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002138 except Exception as exc:
2139 tb = sys.exc_info()[2]
2140 exc.recipe = filename
2141 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002142 return True, None, exc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002143 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
2144 # and for example a worker thread doesn't just exit on its own in response to
2145 # a SystemExit event for example.
2146 except BaseException as exc:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002147 return True, None, ParsingFailure(exc, filename)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002148 finally:
2149 bb.event.LogHandler.filter = origfilter
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002150
2151class CookerParser(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002152 def __init__(self, cooker, mcfilelist, masked):
2153 self.mcfilelist = mcfilelist
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002154 self.cooker = cooker
2155 self.cfgdata = cooker.data
2156 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002157 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002158
2159 # Accounting statistics
2160 self.parsed = 0
2161 self.cached = 0
2162 self.error = 0
2163 self.masked = masked
2164
2165 self.skipped = 0
2166 self.virtuals = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002167
2168 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002169 self.process_names = []
2170
Andrew Geissler5a43b432020-06-13 10:46:56 -05002171 self.bb_caches = bb.cache.MulticonfigCache(self.cfgbuilder, self.cfghash, cooker.caches_array)
2172 self.fromcache = set()
2173 self.willparse = set()
2174 for mc in self.cooker.multiconfigs:
2175 for filename in self.mcfilelist[mc]:
2176 appends = self.cooker.collections[mc].get_file_appends(filename)
2177 if not self.bb_caches[mc].cacheValid(filename, appends):
2178 self.willparse.add((mc, self.bb_caches[mc], filename, appends))
2179 else:
2180 self.fromcache.add((mc, self.bb_caches[mc], filename, appends))
2181
2182 self.total = len(self.fromcache) + len(self.willparse)
2183 self.toparse = len(self.willparse)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002184 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002185
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002186 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Andrew Geissler5a43b432020-06-13 10:46:56 -05002187 multiprocessing.cpu_count()), self.toparse)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002188
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002189 self.start()
2190 self.haveshutdown = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002191 self.syncthread = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002192
2193 def start(self):
2194 self.results = self.load_cached()
2195 self.processes = []
2196 if self.toparse:
2197 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002198
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002199 self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002200 self.result_queue = multiprocessing.Queue()
Brad Bishop19323692019-04-05 15:28:33 -04002201
2202 def chunkify(lst,n):
2203 return [lst[i::n] for i in range(n)]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002204 self.jobs = chunkify(list(self.willparse), self.num_processes)
Brad Bishop19323692019-04-05 15:28:33 -04002205
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002206 for i in range(0, self.num_processes):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002207 parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, self.cooker.configuration.profile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002208 parser.start()
2209 self.process_names.append(parser.name)
2210 self.processes.append(parser)
2211
2212 self.results = itertools.chain(self.results, self.parse_generator())
2213
Andrew Geissler9aee5002022-03-30 16:27:02 +00002214 def shutdown(self, clean=True):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002215 if not self.toparse:
2216 return
2217 if self.haveshutdown:
2218 return
2219 self.haveshutdown = True
2220
2221 if clean:
2222 event = bb.event.ParseCompleted(self.cached, self.parsed,
2223 self.skipped, self.masked,
2224 self.virtuals, self.error,
2225 self.total)
2226
2227 bb.event.fire(event, self.cfgdata)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002228 else:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002229 bb.error("Parsing halted due to errors, see error messages above")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002230
2231 for process in self.processes:
2232 self.parser_quit.put(None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002233
Brad Bishop08902b02019-08-20 09:16:51 -04002234 # Cleanup the queue before call process.join(), otherwise there might be
2235 # deadlocks.
2236 while True:
2237 try:
2238 self.result_queue.get(timeout=0.25)
2239 except queue.Empty:
2240 break
2241
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002242 for process in self.processes:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002243 process.join(0.5)
2244
2245 for process in self.processes:
2246 if process.exitcode is None:
2247 os.kill(process.pid, signal.SIGINT)
2248
2249 for process in self.processes:
2250 process.join(0.5)
2251
2252 for process in self.processes:
2253 if process.exitcode is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002254 process.terminate()
Andrew Geissler9aee5002022-03-30 16:27:02 +00002255
2256 for process in self.processes:
2257 process.join()
2258 # Added in 3.7, cleans up zombies
2259 if hasattr(process, "close"):
2260 process.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002261
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002262 self.parser_quit.close()
2263 # Allow data left in the cancel queue to be discarded
2264 self.parser_quit.cancel_join_thread()
2265
Andrew Geissler5a43b432020-06-13 10:46:56 -05002266 def sync_caches():
2267 for c in self.bb_caches.values():
2268 c.sync()
2269
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002270 sync = threading.Thread(target=sync_caches, name="SyncThread")
2271 self.syncthread = sync
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002272 sync.start()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002273 bb.codeparser.parser_cache_savemerge()
2274 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002275 if self.cooker.configuration.profile:
2276 profiles = []
2277 for i in self.process_names:
2278 logfile = "profile-parse-%s.log" % i
2279 if os.path.exists(logfile):
2280 profiles.append(logfile)
2281
2282 pout = "profile-parse.log.processed"
2283 bb.utils.process_profilelog(profiles, pout = pout)
2284 print("Processed parsing statistics saved to %s" % (pout))
2285
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002286 def final_cleanup(self):
2287 if self.syncthread:
2288 self.syncthread.join()
2289
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002290 def load_cached(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002291 for mc, cache, filename, appends in self.fromcache:
2292 cached, infos = cache.load(filename, appends)
2293 yield not cached, mc, infos
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002294
2295 def parse_generator(self):
Andrew Geissler595f6302022-01-24 19:11:47 +00002296 empty = False
2297 while self.processes or not empty:
2298 for process in self.processes.copy():
2299 if not process.is_alive():
2300 process.join()
2301 self.processes.remove(process)
2302
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002303 if self.parsed >= self.toparse:
2304 break
2305
2306 try:
2307 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002308 except queue.Empty:
Andrew Geissler595f6302022-01-24 19:11:47 +00002309 empty = True
Andrew Geissler9aee5002022-03-30 16:27:02 +00002310 yield None, None, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002311 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002312 empty = False
Andrew Geissler9aee5002022-03-30 16:27:02 +00002313 yield result
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002314
Andrew Geissler595f6302022-01-24 19:11:47 +00002315 if not (self.parsed >= self.toparse):
2316 raise bb.parse.ParseError("Not all recipes parsed, parser thread killed/died? Exiting.", None)
2317
2318
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002319 def parse_next(self):
2320 result = []
2321 parsed = None
2322 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002323 parsed, mc, result = next(self.results)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002324 if isinstance(result, BaseException):
2325 # Turn exceptions back into exceptions
2326 raise result
2327 if parsed is None:
2328 # Timeout, loop back through the main loop
2329 return True
2330
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002331 except StopIteration:
2332 self.shutdown()
2333 return False
2334 except bb.BBHandledException as exc:
2335 self.error += 1
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002336 logger.debug('Failed to parse recipe: %s' % exc.recipe)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002337 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002338 return False
2339 except ParsingFailure as exc:
2340 self.error += 1
2341 logger.error('Unable to parse %s: %s' %
2342 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002343 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002344 return False
2345 except bb.parse.ParseError as exc:
2346 self.error += 1
2347 logger.error(str(exc))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002348 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002349 return False
2350 except bb.data_smart.ExpansionError as exc:
2351 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002352 bbdir = os.path.dirname(__file__) + os.sep
2353 etype, value, _ = sys.exc_info()
2354 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2355 logger.error('ExpansionError during parsing %s', value.recipe,
2356 exc_info=(etype, value, tb))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002357 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002358 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002359 except Exception as exc:
2360 self.error += 1
2361 etype, value, tb = sys.exc_info()
2362 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002363 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002364 exc_info=(etype, value, exc.traceback))
2365 else:
2366 # Most likely, an exception occurred during raising an exception
2367 import traceback
2368 logger.error('Exception during parse: %s' % traceback.format_exc())
Andrew Geissler9aee5002022-03-30 16:27:02 +00002369 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002370 return False
2371
2372 self.current += 1
2373 self.virtuals += len(result)
2374 if parsed:
2375 self.parsed += 1
2376 if self.parsed % self.progress_chunk == 0:
2377 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2378 self.cfgdata)
2379 else:
2380 self.cached += 1
2381
2382 for virtualfn, info_array in result:
2383 if info_array[0].skipped:
2384 self.skipped += 1
2385 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Andrew Geissler5a43b432020-06-13 10:46:56 -05002386 self.bb_caches[mc].add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002387 parsed=parsed, watcher = self.cooker.add_filewatch)
2388 return True
2389
2390 def reparse(self, filename):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002391 to_reparse = set()
2392 for mc in self.cooker.multiconfigs:
2393 to_reparse.add((mc, filename, self.cooker.collections[mc].get_file_appends(filename)))
2394
2395 for mc, filename, appends in to_reparse:
2396 infos = self.bb_caches[mc].parse(filename, appends)
2397 for vfn, info_array in infos:
2398 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)