blob: 2adf4d297df6333c724edd0004ea05f4b1ba3946 [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
Patrick Williamsc124f4f2015-09-15 14:41:29 -050016import threading
Patrick Williamsc0f7c042017-02-23 20:41:17 -060017from io import StringIO, UnsupportedOperation
Patrick Williamsc124f4f2015-09-15 14:41:29 -050018from contextlib import closing
Patrick Williamsc0f7c042017-02-23 20:41:17 -060019from collections import defaultdict, namedtuple
Patrick Williamsc124f4f2015-09-15 14:41:29 -050020import bb, bb.exceptions, bb.command
21from bb import utils, data, parse, event, cache, providers, taskdata, runqueue, build
Patrick Williamsc0f7c042017-02-23 20:41:17 -060022import queue
Patrick Williamsc124f4f2015-09-15 14:41:29 -050023import signal
Patrick Williamsc124f4f2015-09-15 14:41:29 -050024import prserv.serv
25import pyinotify
Patrick Williamsc0f7c042017-02-23 20:41:17 -060026import json
27import pickle
28import codecs
Brad Bishop08902b02019-08-20 09:16:51 -040029import hashserv
Patrick Williamsc124f4f2015-09-15 14:41:29 -050030
31logger = logging.getLogger("BitBake")
32collectlog = logging.getLogger("BitBake.Collection")
33buildlog = logging.getLogger("BitBake.Build")
34parselog = logging.getLogger("BitBake.Parsing")
35providerlog = logging.getLogger("BitBake.Provider")
36
37class NoSpecificMatch(bb.BBHandledException):
38 """
39 Exception raised when no or multiple file matches are found
40 """
41
42class NothingToBuild(Exception):
43 """
44 Exception raised when there is nothing to build
45 """
46
47class CollectionError(bb.BBHandledException):
48 """
49 Exception raised when layer configuration is incorrect
50 """
51
52class state:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060053 initial, parsing, running, shutdown, forceshutdown, stopped, error = list(range(7))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050054
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050055 @classmethod
56 def get_name(cls, code):
57 for name in dir(cls):
58 value = getattr(cls, name)
59 if type(value) == type(cls.initial) and value == code:
60 return name
61 raise ValueError("Invalid status code: %s" % code)
62
Patrick Williamsc124f4f2015-09-15 14:41:29 -050063
64class SkippedPackage:
65 def __init__(self, info = None, reason = None):
66 self.pn = None
67 self.skipreason = None
68 self.provides = None
69 self.rprovides = None
70
71 if info:
72 self.pn = info.pn
73 self.skipreason = info.skipreason
74 self.provides = info.provides
Andrew Geisslerd1e89492021-02-12 15:35:20 -060075 self.rprovides = info.packages + info.rprovides
76 for package in info.packages:
77 self.rprovides += info.rprovides_pkg[package]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050078 elif reason:
79 self.skipreason = reason
80
81
82class CookerFeatures(object):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060083 _feature_list = [HOB_EXTRA_CACHES, BASEDATASTORE_TRACKING, SEND_SANITYEVENTS] = list(range(3))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050084
85 def __init__(self):
86 self._features=set()
87
88 def setFeature(self, f):
89 # validate we got a request for a feature we support
90 if f not in CookerFeatures._feature_list:
91 return
92 self._features.add(f)
93
94 def __contains__(self, f):
95 return f in self._features
96
97 def __iter__(self):
98 return self._features.__iter__()
99
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600100 def __next__(self):
101 return next(self._features)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102
103
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600104class EventWriter:
105 def __init__(self, cooker, eventfile):
106 self.file_inited = None
107 self.cooker = cooker
108 self.eventfile = eventfile
109 self.event_queue = []
110
111 def write_event(self, event):
112 with open(self.eventfile, "a") as f:
113 try:
114 str_event = codecs.encode(pickle.dumps(event), 'base64').decode('utf-8')
115 f.write("%s\n" % json.dumps({"class": event.__module__ + "." + event.__class__.__name__,
116 "vars": str_event}))
117 except Exception as err:
118 import traceback
119 print(err, traceback.format_exc())
120
121 def send(self, event):
122 if self.file_inited:
123 # we have the file, just write the event
124 self.write_event(event)
125 else:
126 # init on bb.event.BuildStarted
127 name = "%s.%s" % (event.__module__, event.__class__.__name__)
128 if name in ("bb.event.BuildStarted", "bb.cooker.CookerExit"):
129 with open(self.eventfile, "w") as f:
130 f.write("%s\n" % json.dumps({ "allvariables" : self.cooker.getAllKeysWithFlags(["doc", "func"])}))
131
132 self.file_inited = True
133
134 # write pending events
135 for evt in self.event_queue:
136 self.write_event(evt)
137
138 # also write the current event
139 self.write_event(event)
140 else:
141 # queue all events until the file is inited
142 self.event_queue.append(event)
143
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500144#============================================================================#
145# BBCooker
146#============================================================================#
147class BBCooker:
148 """
149 Manages one bitbake build run
150 """
151
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500152 def __init__(self, featureSet=None, idleCallBackRegister=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600153 self.recipecaches = None
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500154 self.eventlog = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500155 self.skiplist = {}
156 self.featureset = CookerFeatures()
157 if featureSet:
158 for f in featureSet:
159 self.featureset.setFeature(f)
160
Patrick Williams45852732022-04-02 08:58:32 -0500161 self.orig_syspath = sys.path.copy()
162 self.orig_sysmodules = [*sys.modules]
163
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500164 self.configuration = bb.cookerdata.CookerConfiguration()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500165
Andrew Geissler635e0e42020-08-21 15:58:33 -0500166 self.idleCallBackRegister = idleCallBackRegister
167
Brad Bishopf058f492019-01-28 23:50:33 -0500168 bb.debug(1, "BBCooker starting %s" % time.time())
169 sys.stdout.flush()
170
Patrick Williamsde0582f2022-04-08 10:23:27 -0500171 self.configwatcher = None
172 self.confignotifier = None
Brad Bishopf058f492019-01-28 23:50:33 -0500173
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500174 self.watchmask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CREATE | pyinotify.IN_DELETE | \
175 pyinotify.IN_DELETE_SELF | pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF | \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500176 pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500177
Patrick Williamsde0582f2022-04-08 10:23:27 -0500178 self.watcher = None
179 self.notifier = None
Brad Bishopf058f492019-01-28 23:50:33 -0500180
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500181 # If being called by something like tinfoil, we need to clean cached data
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500182 # which may now be invalid
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500183 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500184 bb.parse.BBHandler.cached_statements = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500185
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500186 self.ui_cmdline = None
Brad Bishop08902b02019-08-20 09:16:51 -0400187 self.hashserv = None
Brad Bishopa34c0302019-09-23 22:34:48 -0400188 self.hashservaddr = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500189
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500190 self.inotify_modified_files = []
191
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000192 def _process_inotify_updates(server, cooker, halt):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500193 cooker.process_inotify_updates()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500194 return 1.0
195
Andrew Geissler635e0e42020-08-21 15:58:33 -0500196 self.idleCallBackRegister(_process_inotify_updates, self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500197
198 # TOSTOP must not be set or our children will hang when they output
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600199 try:
200 fd = sys.stdout.fileno()
201 if os.isatty(fd):
202 import termios
203 tcattr = termios.tcgetattr(fd)
204 if tcattr[3] & termios.TOSTOP:
205 buildlog.info("The terminal had the TOSTOP bit set, clearing...")
206 tcattr[3] = tcattr[3] & ~termios.TOSTOP
207 termios.tcsetattr(fd, termios.TCSANOW, tcattr)
208 except UnsupportedOperation:
209 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500210
211 self.command = bb.command.Command(self)
212 self.state = state.initial
213
214 self.parser = None
215
216 signal.signal(signal.SIGTERM, self.sigterm_exception)
217 # Let SIGHUP exit as SIGTERM
218 signal.signal(signal.SIGHUP, self.sigterm_exception)
219
Brad Bishopf058f492019-01-28 23:50:33 -0500220 bb.debug(1, "BBCooker startup complete %s" % time.time())
221 sys.stdout.flush()
222
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500223 def init_configdata(self):
224 if not hasattr(self, "data"):
225 self.initConfigurationData()
226 bb.debug(1, "BBCooker parsed base configuration %s" % time.time())
227 sys.stdout.flush()
228 self.handlePRServ()
229
Patrick Williamsde0582f2022-04-08 10:23:27 -0500230 def setupConfigWatcher(self):
231 if self.configwatcher:
232 self.configwatcher.close()
233 self.confignotifier = None
234 self.configwatcher = None
235 self.configwatcher = pyinotify.WatchManager()
236 self.configwatcher.bbseen = set()
237 self.configwatcher.bbwatchedfiles = set()
238 self.confignotifier = pyinotify.Notifier(self.configwatcher, self.config_notifications)
239
240 def setupParserWatcher(self):
241 if self.watcher:
242 self.watcher.close()
243 self.notifier = None
244 self.watcher = None
245 self.watcher = pyinotify.WatchManager()
246 self.watcher.bbseen = set()
247 self.watcher.bbwatchedfiles = set()
248 self.notifier = pyinotify.Notifier(self.watcher, self.notifications)
249
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500250 def process_inotify_updates(self):
251 for n in [self.confignotifier, self.notifier]:
Patrick Williamsde0582f2022-04-08 10:23:27 -0500252 if n and n.check_events(timeout=0):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500253 # read notified events and enqeue them
254 n.read_events()
255 n.process_events()
256
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500257 def config_notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500258 if event.maskname == "IN_Q_OVERFLOW":
259 bb.warn("inotify event queue overflowed, invalidating caches.")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500260 self.parsecache_valid = False
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500261 self.baseconfig_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500262 bb.parse.clear_cache()
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500263 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500264 if not event.pathname in self.configwatcher.bbwatchedfiles:
265 return
Andrew Geissler9aee5002022-03-30 16:27:02 +0000266 if "IN_ISDIR" in event.maskname:
Patrick Williams45852732022-04-02 08:58:32 -0500267 if "IN_CREATE" in event.maskname or "IN_DELETE" in event.maskname:
268 if event.pathname in self.configwatcher.bbseen:
269 self.configwatcher.bbseen.remove(event.pathname)
270 # Could remove all entries starting with the directory but for now...
271 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500272 if not event.pathname in self.inotify_modified_files:
273 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274 self.baseconfig_valid = False
275
276 def notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500277 if event.maskname == "IN_Q_OVERFLOW":
278 bb.warn("inotify event queue overflowed, invalidating caches.")
279 self.parsecache_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500280 bb.parse.clear_cache()
281 return
282 if event.pathname.endswith("bitbake-cookerdaemon.log") \
283 or event.pathname.endswith("bitbake.lock"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500284 return
Andrew Geissler9aee5002022-03-30 16:27:02 +0000285 if "IN_ISDIR" in event.maskname:
Patrick Williams45852732022-04-02 08:58:32 -0500286 if "IN_CREATE" in event.maskname or "IN_DELETE" in event.maskname:
287 if event.pathname in self.watcher.bbseen:
288 self.watcher.bbseen.remove(event.pathname)
289 # Could remove all entries starting with the directory but for now...
290 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500291 if not event.pathname in self.inotify_modified_files:
292 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500293 self.parsecache_valid = False
294
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500295 def add_filewatch(self, deps, watcher=None, dirs=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500296 if not watcher:
297 watcher = self.watcher
298 for i in deps:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500299 watcher.bbwatchedfiles.add(i[0])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500300 if dirs:
301 f = i[0]
302 else:
303 f = os.path.dirname(i[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500304 if f in watcher.bbseen:
305 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -0500306 watcher.bbseen.add(f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500307 watchtarget = None
308 while True:
309 # We try and add watches for files that don't exist but if they did, would influence
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500310 # the parser. The parent directory of these files may not exist, in which case we need
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311 # to watch any parent that does exist for changes.
312 try:
313 watcher.add_watch(f, self.watchmask, quiet=False)
314 if watchtarget:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500315 watcher.bbwatchedfiles.add(watchtarget)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500316 break
317 except pyinotify.WatchManagerError as e:
318 if 'ENOENT' in str(e):
319 watchtarget = f
320 f = os.path.dirname(f)
321 if f in watcher.bbseen:
322 break
Andrew Geissler82c905d2020-04-13 13:39:40 -0500323 watcher.bbseen.add(f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500324 continue
325 if 'ENOSPC' in str(e):
326 providerlog.error("No space left on device or exceeds fs.inotify.max_user_watches?")
327 providerlog.error("To check max_user_watches: sysctl -n fs.inotify.max_user_watches.")
328 providerlog.error("To modify max_user_watches: sysctl -n -w fs.inotify.max_user_watches=<value>.")
329 providerlog.error("Root privilege is required to modify max_user_watches.")
330 raise
331
332 def sigterm_exception(self, signum, stackframe):
333 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500334 bb.warn("Cooker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500335 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500336 bb.warn("Cooker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500337 self.state = state.forceshutdown
338
339 def setFeatures(self, features):
340 # we only accept a new feature set if we're in state initial, so we can reset without problems
341 if not self.state in [state.initial, state.shutdown, state.forceshutdown, state.stopped, state.error]:
342 raise Exception("Illegal state for feature set change")
343 original_featureset = list(self.featureset)
344 for feature in features:
345 self.featureset.setFeature(feature)
346 bb.debug(1, "Features set %s (was %s)" % (original_featureset, list(self.featureset)))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500347 if (original_featureset != list(self.featureset)) and self.state != state.error and hasattr(self, "data"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500348 self.reset()
349
350 def initConfigurationData(self):
351
352 self.state = state.initial
353 self.caches_array = []
354
Patrick Williams45852732022-04-02 08:58:32 -0500355 sys.path = self.orig_syspath.copy()
356 for mod in [*sys.modules]:
357 if mod not in self.orig_sysmodules:
358 del sys.modules[mod]
359
Patrick Williamsde0582f2022-04-08 10:23:27 -0500360 self.setupConfigWatcher()
361
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500362 # Need to preserve BB_CONSOLELOG over resets
363 consolelog = None
364 if hasattr(self, "data"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500365 consolelog = self.data.getVar("BB_CONSOLELOG")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500366
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500367 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
368 self.enableDataTracking()
369
370 all_extra_cache_names = []
371 # We hardcode all known cache types in a single place, here.
372 if CookerFeatures.HOB_EXTRA_CACHES in self.featureset:
373 all_extra_cache_names.append("bb.cache_extra:HobRecipeInfo")
374
375 caches_name_array = ['bb.cache:CoreRecipeInfo'] + all_extra_cache_names
376
377 # At least CoreRecipeInfo will be loaded, so caches_array will never be empty!
378 # This is the entry point, no further check needed!
379 for var in caches_name_array:
380 try:
381 module_name, cache_name = var.split(':')
382 module = __import__(module_name, fromlist=(cache_name,))
383 self.caches_array.append(getattr(module, cache_name))
384 except ImportError as exc:
385 logger.critical("Unable to import extra RecipeInfo '%s' from '%s': %s" % (cache_name, module_name, exc))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500386 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500387
388 self.databuilder = bb.cookerdata.CookerDataBuilder(self.configuration, False)
389 self.databuilder.parseBaseConfiguration()
390 self.data = self.databuilder.data
391 self.data_hash = self.databuilder.data_hash
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500392 self.extraconfigdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500393
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500394 if consolelog:
395 self.data.setVar("BB_CONSOLELOG", consolelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500396
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500397 self.data.setVar('BB_CMDLINE', self.ui_cmdline)
398
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500399 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
400 self.disableDataTracking()
401
Brad Bishop15ae2502019-06-18 21:44:24 -0400402 for mc in self.databuilder.mcdata.values():
403 mc.renameVar("__depends", "__base_depends")
404 self.add_filewatch(mc.getVar("__base_depends", False), self.configwatcher)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500405
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500406 self.baseconfig_valid = True
407 self.parsecache_valid = False
408
409 def handlePRServ(self):
410 # Setup a PR Server based on the new configuration
411 try:
412 self.prhost = prserv.serv.auto_start(self.data)
413 except prserv.serv.PRServiceConfigError as e:
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500414 bb.fatal("Unable to start PR Server, exiting, check the bitbake-cookerdaemon.log")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500415
Brad Bishopa34c0302019-09-23 22:34:48 -0400416 if self.data.getVar("BB_HASHSERVE") == "auto":
417 # Create a new hash server bound to a unix domain socket
Brad Bishop08902b02019-08-20 09:16:51 -0400418 if not self.hashserv:
419 dbfile = (self.data.getVar("PERSISTENT_DIR") or self.data.getVar("CACHE")) + "/hashserv.db"
Andrew Geissler595f6302022-01-24 19:11:47 +0000420 upstream = self.data.getVar("BB_HASHSERVE_UPSTREAM") or None
421 if upstream:
422 import socket
423 try:
424 sock = socket.create_connection(upstream.split(":"), 5)
425 sock.close()
426 except socket.error as e:
427 bb.warn("BB_HASHSERVE_UPSTREAM is not valid, unable to connect hash equivalence server at '%s': %s"
428 % (upstream, repr(e)))
429
Brad Bishopa34c0302019-09-23 22:34:48 -0400430 self.hashservaddr = "unix://%s/hashserve.sock" % self.data.getVar("TOPDIR")
Andrew Geissler5199d832021-09-24 16:47:35 -0500431 self.hashserv = hashserv.create_server(
432 self.hashservaddr,
433 dbfile,
434 sync=False,
Andrew Geissler595f6302022-01-24 19:11:47 +0000435 upstream=upstream,
Andrew Geissler5199d832021-09-24 16:47:35 -0500436 )
Patrick Williams213cb262021-08-07 19:21:33 -0500437 self.hashserv.serve_as_process()
Brad Bishopa34c0302019-09-23 22:34:48 -0400438 self.data.setVar("BB_HASHSERVE", self.hashservaddr)
439 self.databuilder.origdata.setVar("BB_HASHSERVE", self.hashservaddr)
440 self.databuilder.data.setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400441 for mc in self.databuilder.mcdata:
Brad Bishopa34c0302019-09-23 22:34:48 -0400442 self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400443
444 bb.parse.init_parser(self.data)
445
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446 def enableDataTracking(self):
447 self.configuration.tracking = True
448 if hasattr(self, "data"):
449 self.data.enableTracking()
450
451 def disableDataTracking(self):
452 self.configuration.tracking = False
453 if hasattr(self, "data"):
454 self.data.disableTracking()
455
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500456 def parseConfiguration(self):
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600457 self.updateCacheSync()
458
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500459 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500460 nice = self.data.getVar("BB_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500461 if nice:
462 curnice = os.nice(0)
463 nice = int(nice) - curnice
464 buildlog.verbose("Renice to %s " % os.nice(nice))
465
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600466 if self.recipecaches:
467 del self.recipecaches
468 self.multiconfigs = self.databuilder.mcdata.keys()
469 self.recipecaches = {}
470 for mc in self.multiconfigs:
471 self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500472
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500473 self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500474
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500475 self.parsecache_valid = False
476
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500477 def updateConfigOpts(self, options, environment, cmdline):
478 self.ui_cmdline = cmdline
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500479 clean = True
480 for o in options:
481 if o in ['prefile', 'postfile']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500482 # Only these options may require a reparse
483 try:
484 if getattr(self.configuration, o) == options[o]:
485 # Value is the same, no need to mark dirty
486 continue
487 except AttributeError:
488 pass
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600489 logger.debug("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500490 print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500492 if hasattr(self.configuration, o):
493 setattr(self.configuration, o, options[o])
494
495 if self.configuration.writeeventlog:
496 if self.eventlog and self.eventlog[0] != self.configuration.writeeventlog:
497 bb.event.unregister_UIHhandler(self.eventlog[1])
498 if not self.eventlog or self.eventlog[0] != self.configuration.writeeventlog:
499 # we log all events to a file if so directed
500 # register the log file writer as UI Handler
501 writer = EventWriter(self, self.configuration.writeeventlog)
502 EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
503 self.eventlog = (self.configuration.writeeventlog, bb.event.register_UIHhandler(EventLogWriteHandler(writer)))
504
505 bb.msg.loggerDefaultLogLevel = self.configuration.default_loglevel
506 bb.msg.loggerDefaultDomains = self.configuration.debug_domains
507
508 if hasattr(self, "data"):
509 origenv = bb.data.init()
510 for k in environment:
511 origenv.setVar(k, environment[k])
512 self.data.setVar("BB_ORIGENV", origenv)
513
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500514 for k in bb.utils.approved_variables():
515 if k in environment and k not in self.configuration.env:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600516 logger.debug("Updating new environment variable %s to %s" % (k, environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500517 self.configuration.env[k] = environment[k]
518 clean = False
519 if k in self.configuration.env and k not in environment:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600520 logger.debug("Updating environment variable %s (deleted)" % (k))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500521 del self.configuration.env[k]
522 clean = False
523 if k not in self.configuration.env and k not in environment:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500524 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500525 if environment[k] != self.configuration.env[k]:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600526 logger.debug("Updating environment variable %s from %s to %s" % (k, self.configuration.env[k], environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500527 self.configuration.env[k] = environment[k]
528 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500529
530 # Now update all the variables not in the datastore to match
531 self.configuration.env = environment
532
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500533 if not clean:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600534 logger.debug("Base environment change, triggering reparse")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500535 self.reset()
536
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000537 def runCommands(self, server, data, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500538 """
539 Run any queued asynchronous command
540 This is done by the idle handler so it runs in true context rather than
541 tied to any UI.
542 """
543
544 return self.command.runAsyncCommand()
545
546 def showVersions(self):
547
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500548 (latest_versions, preferred_versions, required) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500549
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500550 logger.plain("%-35s %25s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version", "Required Version")
551 logger.plain("%-35s %25s %25s %25s\n", "===========", "==============", "=================", "================")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500552
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500553 for p in sorted(self.recipecaches[''].pkg_pn):
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500554 preferred = preferred_versions[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500555 latest = latest_versions[p]
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500556 requiredstr = ""
557 preferredstr = ""
558 if required[p]:
559 if preferred[0] is not None:
560 requiredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
561 else:
562 bb.fatal("REQUIRED_VERSION of package %s not available" % p)
563 else:
564 preferredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500565
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
567
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500568 if preferred == latest:
569 preferredstr = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500570
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500571 logger.plain("%-35s %25s %25s %25s", p, lateststr, preferredstr, requiredstr)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500572
573 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
574 """
575 Show the outer or per-recipe environment
576 """
577 fn = None
578 envdata = None
Brad Bishop15ae2502019-06-18 21:44:24 -0400579 mc = ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500580 if not pkgs_to_build:
581 pkgs_to_build = []
582
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500583 orig_tracking = self.configuration.tracking
584 if not orig_tracking:
585 self.enableDataTracking()
586 self.reset()
Andrew Geissler9aee5002022-03-30 16:27:02 +0000587 # reset() resets to the UI requested value so we have to redo this
588 self.enableDataTracking()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500589
Brad Bishop15ae2502019-06-18 21:44:24 -0400590 def mc_base(p):
591 if p.startswith('mc:'):
592 s = p.split(':')
593 if len(s) == 2:
594 return s[1]
595 return None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500596
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597 if buildfile:
598 # Parse the configuration here. We need to do it explicitly here since
599 # this showEnvironment() code path doesn't use the cache
600 self.parseConfiguration()
601
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600602 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500603 fn = self.matchFile(fn, mc)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600604 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500605 elif len(pkgs_to_build) == 1:
Brad Bishop15ae2502019-06-18 21:44:24 -0400606 mc = mc_base(pkgs_to_build[0])
607 if not mc:
608 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
609 if pkgs_to_build[0] in set(ignore.split()):
610 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500611
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000612 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.halt, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500613
Brad Bishop15ae2502019-06-18 21:44:24 -0400614 mc = runlist[0][0]
615 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616
617 if fn:
618 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -0500619 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
620 envdata = bb_caches[mc].loadDataFull(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500621 except Exception as e:
622 parselog.exception("Unable to read %s", fn)
623 raise
Brad Bishop15ae2502019-06-18 21:44:24 -0400624 else:
625 if not mc in self.databuilder.mcdata:
626 bb.fatal('Not multiconfig named "%s" found' % mc)
627 envdata = self.databuilder.mcdata[mc]
628 data.expandKeys(envdata)
629 parse.ast.runAnonFuncs(envdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500630
631 # Display history
632 with closing(StringIO()) as env:
633 self.data.inchistory.emit(env)
634 logger.plain(env.getvalue())
635
636 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500637 with closing(StringIO()) as env:
638 data.emit_env(env, envdata, True)
639 logger.plain(env.getvalue())
640
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000641 # emit the metadata which isn't valid shell
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500642 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600643 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500644 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500645
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500646 if not orig_tracking:
647 self.disableDataTracking()
648 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500649
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000650 def buildTaskData(self, pkgs_to_build, task, halt, allowincomplete=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 """
652 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
653 """
654 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
655
656 # A task of None means use the default task
657 if task is None:
658 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500659 if not task.startswith("do_"):
660 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500661
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500662 targetlist = self.checkPackages(pkgs_to_build, task)
663 fulltargetlist = []
664 defaulttask_implicit = ''
665 defaulttask_explicit = False
666 wildcard = False
667
668 # Wild card expansion:
Brad Bishop15ae2502019-06-18 21:44:24 -0400669 # Replace string such as "mc:*:bash"
670 # into "mc:A:bash mc:B:bash bash"
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500671 for k in targetlist:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600672 if k.startswith("mc:") and k.count(':') >= 2:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500673 if wildcard:
674 bb.fatal('multiconfig conflict')
675 if k.split(":")[1] == "*":
676 wildcard = True
677 for mc in self.multiconfigs:
678 if mc:
679 fulltargetlist.append(k.replace('*', mc))
680 # implicit default task
681 else:
682 defaulttask_implicit = k.split(":")[2]
683 else:
684 fulltargetlist.append(k)
685 else:
686 defaulttask_explicit = True
687 fulltargetlist.append(k)
688
689 if not defaulttask_explicit and defaulttask_implicit != '':
690 fulltargetlist.append(defaulttask_implicit)
691
692 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600693 taskdata = {}
694 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500695
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600696 for mc in self.multiconfigs:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000697 taskdata[mc] = bb.taskdata.TaskData(halt, skiplist=self.skiplist, allowincomplete=allowincomplete)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600698 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600699 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500700
701 current = 0
702 runlist = []
703 for k in fulltargetlist:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500704 origk = k
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600705 mc = ""
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600706 if k.startswith("mc:") and k.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600707 mc = k.split(":")[1]
708 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500709 ktask = task
710 if ":do_" in k:
711 k2 = k.split(":do_")
712 k = k2[0]
713 ktask = k2[1]
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500714
715 if mc not in self.multiconfigs:
716 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named %s" % (origk, mc))
717
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600718 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500719 current += 1
720 if not ktask.startswith("do_"):
721 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600722 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
723 # e.g. in ASSUME_PROVIDED
724 continue
725 fn = taskdata[mc].build_targets[k][0]
726 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600728
Brad Bishop15ae2502019-06-18 21:44:24 -0400729 havemc = False
730 for mc in self.multiconfigs:
731 if taskdata[mc].get_mcdepends():
732 havemc = True
Brad Bishopf058f492019-01-28 23:50:33 -0500733
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800734 # No need to do check providers if there are no mcdeps or not an mc build
Brad Bishop15ae2502019-06-18 21:44:24 -0400735 if havemc or len(self.multiconfigs) > 1:
Andrew Geissler99467da2019-02-25 18:54:23 -0600736 seen = set()
737 new = True
738 # Make sure we can provide the multiconfig dependency
739 while new:
740 mcdeps = set()
741 # Add unresolved first, so we can get multiconfig indirect dependencies on time
742 for mc in self.multiconfigs:
743 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
744 mcdeps |= set(taskdata[mc].get_mcdepends())
745 new = False
Patrick Williams03907ee2022-05-01 06:28:52 -0500746 for k in mcdeps:
747 if k in seen:
748 continue
749 l = k.split(':')
750 depmc = l[2]
751 if depmc not in self.multiconfigs:
752 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named configuration %s" % (k,depmc))
753 else:
754 logger.debug("Adding providers for multiconfig dependency %s" % l[3])
755 taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
756 seen.add(k)
757 new = True
Brad Bishopf058f492019-01-28 23:50:33 -0500758
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600759 for mc in self.multiconfigs:
760 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
761
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500762 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600763 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500764
765 def prepareTreeData(self, pkgs_to_build, task):
766 """
767 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
768 """
769
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000770 # We set halt to False here to prevent unbuildable targets raising
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600772 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500773
774 return runlist, taskdata
775
776 ######## WARNING : this function requires cache_extra to be enabled ########
777
778 def generateTaskDepTreeData(self, pkgs_to_build, task):
779 """
780 Create a dependency graph of pkgs_to_build including reverse dependency
781 information.
782 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500783 if not task.startswith("do_"):
784 task = "do_%s" % task
785
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500786 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600787 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500788 rq.rqdata.prepare()
789 return self.buildDependTree(rq, taskdata)
790
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600791 @staticmethod
792 def add_mc_prefix(mc, pn):
793 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -0400794 return "mc:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600795 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500796
797 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500799 depend_tree = {}
800 depend_tree["depends"] = {}
801 depend_tree["tdepends"] = {}
802 depend_tree["pn"] = {}
803 depend_tree["rdepends-pn"] = {}
804 depend_tree["packages"] = {}
805 depend_tree["rdepends-pkg"] = {}
806 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500807 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600808 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500809
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600810 for mc in taskdata:
811 for name, fn in list(taskdata[mc].get_providermap().items()):
812 pn = self.recipecaches[mc].pkg_fn[fn]
813 pn = self.add_mc_prefix(mc, pn)
814 if name != pn:
815 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
816 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500817
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600818 for tid in rq.rqdata.runtaskentries:
819 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
820 pn = self.recipecaches[mc].pkg_fn[taskfn]
821 pn = self.add_mc_prefix(mc, pn)
822 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500823 if pn not in depend_tree["pn"]:
824 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600825 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500826 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600827 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500828
829 # if we have extra caches, list all attributes they bring in
830 extra_info = []
831 for cache_class in self.caches_array:
832 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
833 cachefields = getattr(cache_class, 'cachefields', [])
834 extra_info = extra_info + cachefields
835
836 # for all attributes stored, add them to the dependency tree
837 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600838 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500839
840
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500841 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
842 if not dotname in depend_tree["tdepends"]:
843 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600844 for dep in rq.rqdata.runtaskentries[tid].depends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800845 (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
846 deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
Andrew Geissler595f6302022-01-24 19:11:47 +0000847 if depmc:
848 depmc = "mc:" + depmc + ":"
849 depend_tree["tdepends"][dotname].append("%s%s.%s" % (depmc, deppn, bb.runqueue.taskname_from_tid(dep)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600850 if taskfn not in seen_fns:
851 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500852 packages = []
853
854 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600855 for dep in taskdata[mc].depids[taskfn]:
856 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500857
858 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600859 for rdep in taskdata[mc].rdepids[taskfn]:
860 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500861
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600862 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500863 for package in rdepends:
864 depend_tree["rdepends-pkg"][package] = []
865 for rdepend in rdepends[package]:
866 depend_tree["rdepends-pkg"][package].append(rdepend)
867 packages.append(package)
868
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600869 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500870 for package in rrecs:
871 depend_tree["rrecs-pkg"][package] = []
872 for rdepend in rrecs[package]:
873 depend_tree["rrecs-pkg"][package].append(rdepend)
874 if not package in packages:
875 packages.append(package)
876
877 for package in packages:
878 if package not in depend_tree["packages"]:
879 depend_tree["packages"][package] = {}
880 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600881 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500882 depend_tree["packages"][package]["version"] = version
883
884 return depend_tree
885
886 ######## WARNING : this function requires cache_extra to be enabled ########
887 def generatePkgDepTreeData(self, pkgs_to_build, task):
888 """
889 Create a dependency tree of pkgs_to_build, returning the data.
890 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500891 if not task.startswith("do_"):
892 task = "do_%s" % task
893
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500894 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500895
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600896 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500897 depend_tree = {}
898 depend_tree["depends"] = {}
899 depend_tree["pn"] = {}
900 depend_tree["rdepends-pn"] = {}
901 depend_tree["rdepends-pkg"] = {}
902 depend_tree["rrecs-pkg"] = {}
903
904 # if we have extra caches, list all attributes they bring in
905 extra_info = []
906 for cache_class in self.caches_array:
907 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
908 cachefields = getattr(cache_class, 'cachefields', [])
909 extra_info = extra_info + cachefields
910
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600911 tids = []
912 for mc in taskdata:
913 for tid in taskdata[mc].taskentries:
914 tids.append(tid)
915
916 for tid in tids:
917 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
918
919 pn = self.recipecaches[mc].pkg_fn[taskfn]
920 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500921
922 if pn not in depend_tree["pn"]:
923 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600924 depend_tree["pn"][pn]["filename"] = taskfn
925 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500926 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600927 rdepends = self.recipecaches[mc].rundeps[taskfn]
928 rrecs = self.recipecaches[mc].runrecs[taskfn]
929 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500930
931 # for all extra attributes stored, add them to the dependency tree
932 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600933 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500934
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600935 if taskfn not in seen_fns:
936 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500937
938 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500939 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500940 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600941 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
942 fn_provider = taskdata[mc].build_targets[dep][0]
943 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500944 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500945 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600946 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500947 depend_tree["depends"][pn].append(pn_provider)
948
949 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600950 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500951 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600952 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
953 fn_rprovider = taskdata[mc].run_targets[rdep][0]
954 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500955 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600956 pn_rprovider = rdep
957 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500958 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
959
960 depend_tree["rdepends-pkg"].update(rdepends)
961 depend_tree["rrecs-pkg"].update(rrecs)
962
963 return depend_tree
964
965 def generateDepTreeEvent(self, pkgs_to_build, task):
966 """
967 Create a task dependency graph of pkgs_to_build.
968 Generate an event with the result
969 """
970 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
971 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
972
973 def generateDotGraphFiles(self, pkgs_to_build, task):
974 """
975 Create a task dependency graph of pkgs_to_build.
976 Save the result to a set of .dot files.
977 """
978
979 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
980
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500981 with open('pn-buildlist', 'w') as f:
982 for pn in depgraph["pn"]:
983 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500984 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500985
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500986 # Remove old format output files to ensure no confusion with stale data
987 try:
988 os.unlink('pn-depends.dot')
989 except FileNotFoundError:
990 pass
991 try:
992 os.unlink('package-depends.dot')
993 except FileNotFoundError:
994 pass
Brad Bishop79641f22019-09-10 07:20:22 -0400995 try:
996 os.unlink('recipe-depends.dot')
997 except FileNotFoundError:
998 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500999
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001000 with open('task-depends.dot', 'w') as f:
1001 f.write("digraph depends {\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -04001002 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001003 (pn, taskname) = task.rsplit(".", 1)
1004 fn = depgraph["pn"][pn]["filename"]
1005 version = depgraph["pn"][pn]["version"]
1006 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 -04001007 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001008 f.write('"%s" -> "%s"\n' % (task, dep))
1009 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001010 logger.info("Task dependencies saved to 'task-depends.dot'")
1011
1012 def show_appends_with_no_recipes(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001013 appends_without_recipes = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001014 # Determine which bbappends haven't been applied
Andrew Geissler5a43b432020-06-13 10:46:56 -05001015 for mc in self.multiconfigs:
1016 # First get list of recipes, including skipped
1017 recipefns = list(self.recipecaches[mc].pkg_fn.keys())
1018 recipefns.extend(self.skiplist.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001019
Andrew Geissler5a43b432020-06-13 10:46:56 -05001020 # Work out list of bbappends that have been applied
1021 applied_appends = []
1022 for fn in recipefns:
1023 applied_appends.extend(self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001024
Andrew Geissler5a43b432020-06-13 10:46:56 -05001025 appends_without_recipes[mc] = []
1026 for _, appendfn in self.collections[mc].bbappends:
1027 if not appendfn in applied_appends:
1028 appends_without_recipes[mc].append(appendfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001029
Andrew Geissler5a43b432020-06-13 10:46:56 -05001030 msgs = []
1031 for mc in sorted(appends_without_recipes.keys()):
1032 if appends_without_recipes[mc]:
1033 msgs.append('No recipes in %s available for:\n %s' % (mc if mc else 'default',
1034 '\n '.join(appends_without_recipes[mc])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001035
Andrew Geissler5a43b432020-06-13 10:46:56 -05001036 if msgs:
1037 msg = "\n".join(msgs)
1038 warn_only = self.databuilder.mcdata[mc].getVar("BB_DANGLINGAPPENDS_WARNONLY", \
1039 False) or "no"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001040 if warn_only.lower() in ("1", "yes", "true"):
1041 bb.warn(msg)
1042 else:
1043 bb.fatal(msg)
1044
1045 def handlePrefProviders(self):
1046
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001047 for mc in self.multiconfigs:
1048 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001049 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001050
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001051 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001052 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001053 try:
1054 (providee, provider) = p.split(':')
1055 except:
1056 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
1057 continue
1058 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
1059 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
1060 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001061
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001062 def findConfigFilePath(self, configfile):
1063 """
1064 Find the location on disk of configfile and if it exists and was parsed by BitBake
1065 emit the ConfigFilePathFound event with the path to the file.
1066 """
1067 path = bb.cookerdata.findConfigFile(configfile, self.data)
1068 if not path:
1069 return
1070
1071 # Generate a list of parsed configuration files by searching the files
1072 # listed in the __depends and __base_depends variables with a .conf suffix.
1073 conffiles = []
1074 dep_files = self.data.getVar('__base_depends', False) or []
1075 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
1076
1077 for f in dep_files:
1078 if f[0].endswith(".conf"):
1079 conffiles.append(f[0])
1080
1081 _, conf, conffile = path.rpartition("conf/")
1082 match = os.path.join(conf, conffile)
1083 # Try and find matches for conf/conffilename.conf as we don't always
1084 # have the full path to the file.
1085 for cfg in conffiles:
1086 if cfg.endswith(match):
1087 bb.event.fire(bb.event.ConfigFilePathFound(path),
1088 self.data)
1089 break
1090
1091 def findFilesMatchingInDir(self, filepattern, directory):
1092 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001093 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001094 'directory' in each BBPATH. i.e. to find all rootfs package classes available
1095 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
1096 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001097 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001098 """
1099
1100 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001101 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001102 for path in bbpaths:
1103 dirpath = os.path.join(path, directory)
1104 if os.path.exists(dirpath):
1105 for root, dirs, files in os.walk(dirpath):
1106 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001107 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001108 matches.append(f)
1109
1110 if matches:
1111 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1112
Patrick Williams93c203f2021-10-06 16:15:23 -05001113 def testCookerCommandEvent(self, filepattern):
1114 # Dummy command used by OEQA selftest to test tinfoil without IO
1115 matches = ["A", "B"]
1116 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1117
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001118 def findProviders(self, mc=''):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001119 return bb.providers.findProviders(self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001120
1121 def findBestProvider(self, pn, mc=''):
1122 if pn in self.recipecaches[mc].providers:
1123 filenames = self.recipecaches[mc].providers[pn]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001124 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.databuilder.mcdata[mc], self.recipecaches[mc])
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001125 if eligible is not None:
1126 filename = eligible[0]
1127 else:
1128 filename = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001129 return None, None, None, filename
1130 elif pn in self.recipecaches[mc].pkg_pn:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001131 (latest, latest_f, preferred_ver, preferred_file, required) = bb.providers.findBestProvider(pn, self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
1132 if required and preferred_file is None:
1133 return None, None, None, None
1134 return (latest, latest_f, preferred_ver, preferred_file)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001135 else:
1136 return None, None, None, None
1137
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001138 def findConfigFiles(self, varname):
1139 """
1140 Find config files which are appropriate values for varname.
1141 i.e. MACHINE, DISTRO
1142 """
1143 possible = []
1144 var = varname.lower()
1145
1146 data = self.data
1147 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001148 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001149 for path in bbpaths:
1150 confpath = os.path.join(path, "conf", var)
1151 if os.path.exists(confpath):
1152 for root, dirs, files in os.walk(confpath):
1153 # get all child files, these are appropriate values
1154 for f in files:
1155 val, sep, end = f.rpartition('.')
1156 if end == 'conf':
1157 possible.append(val)
1158
1159 if possible:
1160 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1161
1162 def findInheritsClass(self, klass):
1163 """
1164 Find all recipes which inherit the specified class
1165 """
1166 pkg_list = []
1167
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001168 for pfn in self.recipecaches[''].pkg_fn:
1169 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001170 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001171 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001172
1173 return pkg_list
1174
1175 def generateTargetsTree(self, klass=None, pkgs=None):
1176 """
1177 Generate a dependency tree of buildable targets
1178 Generate an event with the result
1179 """
1180 # if the caller hasn't specified a pkgs list default to universe
1181 if not pkgs:
1182 pkgs = ['universe']
1183 # if inherited_class passed ensure all recipes which inherit the
1184 # specified class are included in pkgs
1185 if klass:
1186 extra_pkgs = self.findInheritsClass(klass)
1187 pkgs = pkgs + extra_pkgs
1188
1189 # generate a dependency tree for all our packages
1190 tree = self.generatePkgDepTreeData(pkgs, 'build')
1191 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1192
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001193 def interactiveMode( self ):
1194 """Drop off into a shell"""
1195 try:
1196 from bb import shell
1197 except ImportError:
1198 parselog.exception("Interactive mode not available")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001199 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001200 else:
1201 shell.start( self )
1202
1203
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001204 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001205 """Handle collections"""
1206 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001207 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001208 if collections:
1209 collection_priorities = {}
1210 collection_depends = {}
1211 collection_list = collections.split()
1212 min_prio = 0
1213 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001214 bb.debug(1,'Processing %s in collection list' % (c))
1215
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001216 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001217 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001218 if priority:
1219 try:
1220 prio = int(priority)
1221 except ValueError:
1222 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1223 errors = True
1224 if min_prio == 0 or prio < min_prio:
1225 min_prio = prio
1226 collection_priorities[c] = prio
1227 else:
1228 collection_priorities[c] = None
1229
1230 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001231 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001232 if deps:
1233 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001234 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001235 except bb.utils.VersionStringException as vse:
1236 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001237 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001238 if dep in collection_list:
1239 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001240 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001241 (op, depver) = opstr.split()
1242 if layerver:
1243 try:
1244 res = bb.utils.vercmp_string_op(layerver, depver, op)
1245 except bb.utils.VersionStringException as vse:
1246 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1247 if not res:
1248 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)
1249 errors = True
1250 else:
1251 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)
1252 errors = True
1253 else:
1254 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1255 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001256 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001257 else:
1258 collection_depends[c] = []
1259
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001260 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001261 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001262 if recs:
1263 try:
1264 recDict = bb.utils.explode_dep_versions2(recs)
1265 except bb.utils.VersionStringException as vse:
1266 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1267 for rec, oplist in list(recDict.items()):
1268 if rec in collection_list:
1269 if oplist:
1270 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001271 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001272 if layerver:
1273 (op, recver) = opstr.split()
1274 try:
1275 res = bb.utils.vercmp_string_op(layerver, recver, op)
1276 except bb.utils.VersionStringException as vse:
1277 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1278 if not res:
1279 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)
1280 continue
1281 else:
1282 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)
1283 continue
1284 parselog.debug(3,"Layer '%s' recommends layer '%s', so we are adding it", c, rec)
1285 collection_depends[c].append(rec)
1286 else:
1287 parselog.debug(3,"Layer '%s' recommends layer '%s', but this layer is not enabled in your configuration", c, rec)
1288
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001289 # Recursively work out collection priorities based on dependencies
1290 def calc_layer_priority(collection):
1291 if not collection_priorities[collection]:
1292 max_depprio = min_prio
1293 for dep in collection_depends[collection]:
1294 calc_layer_priority(dep)
1295 depprio = collection_priorities[dep]
1296 if depprio > max_depprio:
1297 max_depprio = depprio
1298 max_depprio += 1
1299 parselog.debug(1, "Calculated priority of layer %s as %d", collection, max_depprio)
1300 collection_priorities[collection] = max_depprio
1301
1302 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1303 for c in collection_list:
1304 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001305 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001306 if regex is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001307 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1308 errors = True
1309 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001310 elif regex == "":
1311 parselog.debug(1, "BBFILE_PATTERN_%s is empty" % c)
Brad Bishop19323692019-04-05 15:28:33 -04001312 cre = re.compile('^NULL$')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001313 errors = False
1314 else:
1315 try:
1316 cre = re.compile(regex)
1317 except re.error:
1318 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1319 errors = True
1320 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001321 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001322 if errors:
1323 # We've already printed the actual error(s)
1324 raise CollectionError("Errors during parsing layer configuration")
1325
1326 def buildSetVars(self):
1327 """
1328 Setup any variables needed before starting a build
1329 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001330 t = time.gmtime()
1331 for mc in self.databuilder.mcdata:
1332 ds = self.databuilder.mcdata[mc]
1333 if not ds.getVar("BUILDNAME", False):
1334 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1335 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1336 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1337 ds.setVar("TIME", time.strftime('%H%M%S', t))
1338
1339 def reset_mtime_caches(self):
1340 """
1341 Reset mtime caches - this is particularly important when memory resident as something
1342 which is cached is not unlikely to have changed since the last invocation (e.g. a
1343 file associated with a recipe might have been modified by the user).
1344 """
1345 build.reset_cache()
1346 bb.fetch._checksum_cache.mtime_cache.clear()
1347 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1348 if siggen_cache:
1349 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001350
Andrew Geissler5a43b432020-06-13 10:46:56 -05001351 def matchFiles(self, bf, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001352 """
1353 Find the .bb files which match the expression in 'buildfile'.
1354 """
1355 if bf.startswith("/") or bf.startswith("../"):
1356 bf = os.path.abspath(bf)
1357
Andrew Geissler5a43b432020-06-13 10:46:56 -05001358 self.collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)}
1359 filelist, masked, searchdirs = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001360 try:
1361 os.stat(bf)
1362 bf = os.path.abspath(bf)
1363 return [bf]
1364 except OSError:
1365 regexp = re.compile(bf)
1366 matches = []
1367 for f in filelist:
1368 if regexp.search(f) and os.path.isfile(f):
1369 matches.append(f)
1370 return matches
1371
Andrew Geissler5a43b432020-06-13 10:46:56 -05001372 def matchFile(self, buildfile, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001373 """
1374 Find the .bb file which matches the expression in 'buildfile'.
1375 Raise an error if multiple files
1376 """
Andrew Geissler5a43b432020-06-13 10:46:56 -05001377 matches = self.matchFiles(buildfile, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001378 if len(matches) != 1:
1379 if matches:
1380 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1381 if matches:
1382 for f in matches:
1383 msg += "\n %s" % f
1384 parselog.error(msg)
1385 else:
1386 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1387 raise NoSpecificMatch
1388 return matches[0]
1389
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001390 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001391 """
1392 Build the file matching regexp buildfile
1393 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001394 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001395
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001396 # Too many people use -b because they think it's how you normally
1397 # specify a target to be built, so show a warning
1398 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1399
1400 self.buildFileInternal(buildfile, task)
1401
1402 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1403 """
1404 Build the file matching regexp buildfile
1405 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001406
1407 # Parse the configuration here. We need to do it explicitly here since
1408 # buildFile() doesn't use the cache
1409 self.parseConfiguration()
1410
1411 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001412 if task is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001413 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001414 if not task.startswith("do_"):
1415 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001416
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001417 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001418 fn = self.matchFile(fn, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001419
1420 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001421 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001422
Andrew Geissler5a43b432020-06-13 10:46:56 -05001423 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001424
Andrew Geissler5a43b432020-06-13 10:46:56 -05001425 infos = bb_caches[mc].parse(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001426 infos = dict(infos)
1427
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001428 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001429 try:
1430 info_array = infos[fn]
1431 except KeyError:
1432 bb.fatal("%s does not exist" % fn)
1433
1434 if info_array[0].skipped:
1435 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1436
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001437 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001438
1439 # Tweak some variables
1440 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001441 self.recipecaches[mc].ignored_dependencies = set()
1442 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001443 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001444
1445 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001446 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1447 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001448 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1449 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001450
1451 # Invalidate task for target if force mode active
1452 if self.configuration.force:
1453 logger.verbose("Invalidate task %s, %s", task, fn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001454 bb.parse.siggen.invalidate_task(task, self.recipecaches[mc], fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001455
1456 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001457 taskdata = {}
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001458 taskdata[mc] = bb.taskdata.TaskData(self.configuration.halt)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001459 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001460
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001461 if quietlog:
1462 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1463 bb.runqueue.logger.setLevel(logging.WARNING)
1464
1465 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1466 if fireevents:
1467 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001468
1469 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001470 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001471
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001472 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001473
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001474 def buildFileIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001475
1476 msg = None
1477 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001478 if halt or self.state == state.forceshutdown:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001479 rq.finish_runqueue(True)
1480 msg = "Forced shutdown"
1481 interrupted = 2
1482 elif self.state == state.shutdown:
1483 rq.finish_runqueue(False)
1484 msg = "Stopped build"
1485 interrupted = 1
1486 failures = 0
1487 try:
1488 retval = rq.execute_runqueue()
1489 except runqueue.TaskFailure as exc:
1490 failures += len(exc.args)
1491 retval = False
1492 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001493 self.command.finishAsyncCommand(str(exc))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001494 if quietlog:
1495 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001496 return False
1497
1498 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001499 if fireevents:
1500 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 -05001501 self.command.finishAsyncCommand(msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001502 # We trashed self.recipecaches above
1503 self.parsecache_valid = False
1504 self.configuration.limited_deps = False
1505 bb.parse.siggen.reset(self.data)
1506 if quietlog:
1507 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001508 return False
1509 if retval is True:
1510 return True
1511 return retval
1512
Andrew Geissler635e0e42020-08-21 15:58:33 -05001513 self.idleCallBackRegister(buildFileIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001514
1515 def buildTargets(self, targets, task):
1516 """
1517 Attempt to build the targets specified
1518 """
1519
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001520 def buildTargetsIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001521 msg = None
1522 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001523 if halt or self.state == state.forceshutdown:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001524 rq.finish_runqueue(True)
1525 msg = "Forced shutdown"
1526 interrupted = 2
1527 elif self.state == state.shutdown:
1528 rq.finish_runqueue(False)
1529 msg = "Stopped build"
1530 interrupted = 1
1531 failures = 0
1532 try:
1533 retval = rq.execute_runqueue()
1534 except runqueue.TaskFailure as exc:
1535 failures += len(exc.args)
1536 retval = False
1537 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001538 self.command.finishAsyncCommand(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001539 return False
1540
1541 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001542 try:
1543 for mc in self.multiconfigs:
1544 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1545 finally:
1546 self.command.finishAsyncCommand(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001547 return False
1548 if retval is True:
1549 return True
1550 return retval
1551
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001552 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001553 self.buildSetVars()
1554
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001555 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001556 if task is None:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001557 task = self.configuration.cmd
1558
1559 if not task.startswith("do_"):
1560 task = "do_%s" % task
1561
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001562 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1563
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001564 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001565
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001566 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.halt)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001567
1568 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001569
1570 # make targets to always look as <target>:do_<task>
1571 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001572 for target in runlist:
1573 if target[0]:
Brad Bishop15ae2502019-06-18 21:44:24 -04001574 ntargets.append("mc:%s:%s:%s" % (target[0], target[1], target[2]))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001575 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001576
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001577 for mc in self.multiconfigs:
1578 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001579
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001580 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001581 if 'universe' in targets:
1582 rq.rqdata.warn_multi_bb = True
1583
Andrew Geissler635e0e42020-08-21 15:58:33 -05001584 self.idleCallBackRegister(buildTargetsIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001585
1586
1587 def getAllKeysWithFlags(self, flaglist):
1588 dump = {}
1589 for k in self.data.keys():
1590 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001591 expand = True
1592 flags = self.data.getVarFlags(k)
1593 if flags and "func" in flags and "python" in flags:
1594 expand = False
1595 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001596 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1597 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001598 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001599 'history' : self.data.varhistory.variable(k),
1600 }
1601 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001602 if flags and d in flags:
1603 dump[k][d] = flags[d]
1604 else:
1605 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001606 except Exception as e:
1607 print(e)
1608 return dump
1609
1610
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001611 def updateCacheSync(self):
1612 if self.state == state.running:
1613 return
1614
1615 # reload files for which we got notifications
1616 for p in self.inotify_modified_files:
1617 bb.parse.update_cache(p)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001618 if p in bb.parse.BBHandler.cached_statements:
1619 del bb.parse.BBHandler.cached_statements[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001620 self.inotify_modified_files = []
1621
1622 if not self.baseconfig_valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001623 logger.debug("Reloading base configuration data")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001624 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001625 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001626
1627 # This is called for all async commands when self.state != running
1628 def updateCache(self):
1629 if self.state == state.running:
1630 return
1631
1632 if self.state in (state.shutdown, state.forceshutdown, state.error):
1633 if hasattr(self.parser, 'shutdown'):
Andrew Geissler9aee5002022-03-30 16:27:02 +00001634 self.parser.shutdown(clean=False)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001635 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001636 raise bb.BBHandledException()
1637
1638 if self.state != state.parsing:
1639 self.updateCacheSync()
1640
1641 if self.state != state.parsing and not self.parsecache_valid:
Patrick Williamsde0582f2022-04-08 10:23:27 -05001642 self.setupParserWatcher()
1643
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001644 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001645 self.parseConfiguration ()
1646 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001647 for mc in self.multiconfigs:
1648 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001649
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001650 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001651 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001652 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001653
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001654 for dep in self.configuration.extra_assume_provided:
1655 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001656
Andrew Geissler5a43b432020-06-13 10:46:56 -05001657 self.collections = {}
1658
1659 mcfilelist = {}
1660 total_masked = 0
1661 searchdirs = set()
1662 for mc in self.multiconfigs:
1663 self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc)
1664 (filelist, masked, search) = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
1665
1666 mcfilelist[mc] = filelist
1667 total_masked += masked
1668 searchdirs |= set(search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001669
1670 # Add inotify watches for directories searched for bb/bbappend files
1671 for dirent in searchdirs:
1672 self.add_filewatch([[dirent]], dirs=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001673
Andrew Geissler5a43b432020-06-13 10:46:56 -05001674 self.parser = CookerParser(self, mcfilelist, total_masked)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001675 self.parsecache_valid = True
1676
1677 self.state = state.parsing
1678
1679 if not self.parser.parse_next():
1680 collectlog.debug(1, "parsing complete")
1681 if self.parser.error:
1682 raise bb.BBHandledException()
1683 self.show_appends_with_no_recipes()
1684 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001685 for mc in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001686 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 -05001687 self.state = state.running
1688
1689 # Send an event listing all stamps reachable after parsing
1690 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001691 for mc in self.multiconfigs:
1692 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1693 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001694 return None
1695
1696 return True
1697
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001698 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001699
1700 # Return a copy, don't modify the original
1701 pkgs_to_build = pkgs_to_build[:]
1702
Andrew Geissler595f6302022-01-24 19:11:47 +00001703 if not pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001704 raise NothingToBuild
1705
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001706 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001707 for pkg in pkgs_to_build.copy():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001708 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001709 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Brad Bishop15ae2502019-06-18 21:44:24 -04001710 if pkg.startswith("multiconfig:"):
1711 pkgs_to_build.remove(pkg)
1712 pkgs_to_build.append(pkg.replace("multiconfig:", "mc:"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001713
1714 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001715 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001716 for mc in self.multiconfigs:
1717 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1718 for t in self.recipecaches[mc].world_target:
1719 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001720 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001721 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001722
1723 if 'universe' in pkgs_to_build:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001724 parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001725 parselog.debug(1, "collating packages for \"universe\"")
1726 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001727 for mc in self.multiconfigs:
1728 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001729 if task:
1730 foundtask = False
1731 for provider_fn in self.recipecaches[mc].providers[t]:
1732 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1733 foundtask = True
1734 break
1735 if not foundtask:
1736 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1737 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001738 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001739 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001740 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001741
1742 return pkgs_to_build
1743
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001744 def pre_serve(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001745 return
1746
1747 def post_serve(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001748 self.shutdown(force=True)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001749 prserv.serv.auto_shutdown()
Patrick Williams45852732022-04-02 08:58:32 -05001750 if hasattr(bb.parse, "siggen"):
1751 bb.parse.siggen.exit()
Brad Bishop08902b02019-08-20 09:16:51 -04001752 if self.hashserv:
1753 self.hashserv.process.terminate()
1754 self.hashserv.process.join()
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001755 if hasattr(self, "data"):
1756 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001757
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001758 def shutdown(self, force = False):
1759 if force:
1760 self.state = state.forceshutdown
1761 else:
1762 self.state = state.shutdown
1763
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001764 if self.parser:
Andrew Geissler9aee5002022-03-30 16:27:02 +00001765 self.parser.shutdown(clean=not force)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001766 self.parser.final_cleanup()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001767
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001768 def finishcommand(self):
1769 self.state = state.initial
1770
1771 def reset(self):
Patrick Williams45852732022-04-02 08:58:32 -05001772 if hasattr(bb.parse, "siggen"):
1773 bb.parse.siggen.exit()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001774 self.initConfigurationData()
Brad Bishop08902b02019-08-20 09:16:51 -04001775 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001776
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001777 def clientComplete(self):
1778 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001779 self.finishcommand()
1780 self.extraconfigdata = {}
1781 self.command.reset()
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001782 if hasattr(self, "data"):
1783 self.databuilder.reset()
1784 self.data = self.databuilder.data
Andrew Geissler82c905d2020-04-13 13:39:40 -05001785 self.parsecache_valid = False
1786 self.baseconfig_valid = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001787
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001788
1789class CookerExit(bb.event.Event):
1790 """
1791 Notify clients of the Cooker shutdown
1792 """
1793
1794 def __init__(self):
1795 bb.event.Event.__init__(self)
1796
1797
1798class CookerCollectFiles(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001799 def __init__(self, priorities, mc=''):
1800 self.mc = mc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001801 self.bbappends = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001802 # Priorities is a list of tuples, with the second element as the pattern.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001803 # We need to sort the list with the longest pattern first, and so on to
1804 # the shortest. This allows nested layers to be properly evaluated.
1805 self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001806
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001807 def calc_bbfile_priority(self, filename):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001808 for _, _, regex, pri in self.bbfile_config_priorities:
1809 if regex.match(filename):
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001810 return pri, regex
1811 return 0, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001812
1813 def get_bbfiles(self):
1814 """Get list of default .bb files by reading out the current directory"""
1815 path = os.getcwd()
1816 contents = os.listdir(path)
1817 bbfiles = []
1818 for f in contents:
1819 if f.endswith(".bb"):
1820 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1821 return bbfiles
1822
1823 def find_bbfiles(self, path):
1824 """Find all the .bb and .bbappend files in a directory"""
1825 found = []
1826 for dir, dirs, files in os.walk(path):
1827 for ignored in ('SCCS', 'CVS', '.svn'):
1828 if ignored in dirs:
1829 dirs.remove(ignored)
1830 found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
1831
1832 return found
1833
1834 def collect_bbfiles(self, config, eventdata):
1835 """Collect all available .bb build files"""
1836 masked = 0
1837
1838 collectlog.debug(1, "collecting .bb files")
1839
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001840 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001841
1842 # Sort files by priority
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001843 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem)[0] )
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001844 config.setVar("BBFILES_PRIORITIZED", " ".join(files))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001845
Andrew Geissler595f6302022-01-24 19:11:47 +00001846 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001847 files = self.get_bbfiles()
1848
Andrew Geissler595f6302022-01-24 19:11:47 +00001849 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001850 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1851 bb.event.fire(CookerExit(), eventdata)
1852
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001853 # We need to track where we look so that we can add inotify watches. There
1854 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001855 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001856 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001857 if hasattr(os, 'scandir'):
1858 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001859 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001860
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001861 def ourlistdir(d):
1862 searchdirs.append(d)
1863 return origlistdir(d)
1864
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001865 def ourscandir(d):
1866 searchdirs.append(d)
1867 return origscandir(d)
1868
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001869 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001870 if hasattr(os, 'scandir'):
1871 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001872 try:
1873 # Can't use set here as order is important
1874 newfiles = []
1875 for f in files:
1876 if os.path.isdir(f):
1877 dirfiles = self.find_bbfiles(f)
1878 for g in dirfiles:
1879 if g not in newfiles:
1880 newfiles.append(g)
1881 else:
1882 globbed = glob.glob(f)
1883 if not globbed and os.path.exists(f):
1884 globbed = [f]
1885 # glob gives files in order on disk. Sort to be deterministic.
1886 for g in sorted(globbed):
1887 if g not in newfiles:
1888 newfiles.append(g)
1889 finally:
1890 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001891 if hasattr(os, 'scandir'):
1892 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001893
1894 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001895
1896 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001897 # First validate the individual regular expressions and ignore any
1898 # that do not compile
1899 bbmasks = []
1900 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001901 # When constructing an older style single regex, it's possible for BBMASK
1902 # to end up beginning with '|', which matches and masks _everything_.
1903 if mask.startswith("|"):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001904 collectlog.warning("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001905 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001906 try:
1907 re.compile(mask)
1908 bbmasks.append(mask)
Andrew Geissler78b72792022-06-14 06:47:25 -05001909 except re.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001910 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1911
1912 # Then validate the combined regular expressions. This should never
1913 # fail, but better safe than sorry...
1914 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001915 try:
1916 bbmask_compiled = re.compile(bbmask)
Andrew Geissler78b72792022-06-14 06:47:25 -05001917 except re.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001918 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1919 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001920
1921 bbfiles = []
1922 bbappend = []
1923 for f in newfiles:
1924 if bbmask and bbmask_compiled.search(f):
1925 collectlog.debug(1, "skipping masked file %s", f)
1926 masked += 1
1927 continue
1928 if f.endswith('.bb'):
1929 bbfiles.append(f)
1930 elif f.endswith('.bbappend'):
1931 bbappend.append(f)
1932 else:
1933 collectlog.debug(1, "skipping %s: unknown file extension", f)
1934
1935 # Build a list of .bbappend files for each .bb file
1936 for f in bbappend:
1937 base = os.path.basename(f).replace('.bbappend', '.bb')
1938 self.bbappends.append((base, f))
1939
1940 # Find overlayed recipes
1941 # bbfiles will be in priority order which makes this easy
1942 bbfile_seen = dict()
1943 self.overlayed = defaultdict(list)
1944 for f in reversed(bbfiles):
1945 base = os.path.basename(f)
1946 if base not in bbfile_seen:
1947 bbfile_seen[base] = f
1948 else:
1949 topfile = bbfile_seen[base]
1950 self.overlayed[topfile].append(f)
1951
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001952 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001953
1954 def get_file_appends(self, fn):
1955 """
1956 Returns a list of .bbappend files to apply to fn
1957 """
1958 filelist = []
1959 f = os.path.basename(fn)
1960 for b in self.bbappends:
1961 (bbappend, filename) = b
1962 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1963 filelist.append(filename)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001964 return tuple(filelist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001965
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001966 def collection_priorities(self, pkgfns, fns, d):
1967 # Return the priorities of the entries in pkgfns
1968 # Also check that all the regexes in self.bbfile_config_priorities are used
1969 # (but to do that we need to ensure skipped recipes aren't counted, nor
1970 # collections in BBFILE_PATTERN_IGNORE_EMPTY)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001971
1972 priorities = {}
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001973 seen = set()
1974 matched = set()
1975
1976 matched_regex = set()
1977 unmatched_regex = set()
1978 for _, _, regex, _ in self.bbfile_config_priorities:
1979 unmatched_regex.add(regex)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001980
1981 # Calculate priorities for each file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001982 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001983 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001984 priorities[p], regex = self.calc_bbfile_priority(realfn)
1985 if regex in unmatched_regex:
1986 matched_regex.add(regex)
1987 unmatched_regex.remove(regex)
1988 seen.add(realfn)
1989 if regex:
1990 matched.add(realfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001991
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001992 if unmatched_regex:
1993 # Account for bbappend files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001994 for b in self.bbappends:
1995 (bbfile, append) = b
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001996 seen.add(append)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001997
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001998 # Account for skipped recipes
1999 seen.update(fns)
2000
2001 seen.difference_update(matched)
2002
2003 def already_matched(fn):
2004 for regex in matched_regex:
2005 if regex.match(fn):
2006 return True
2007 return False
2008
2009 for unmatch in unmatched_regex.copy():
2010 for fn in seen:
2011 if unmatch.match(fn):
2012 # If the bbappend or file was already matched by another regex, skip it
2013 # e.g. for a layer within a layer, the outer regex could match, the inner
2014 # regex may match nothing and we should warn about that
2015 if already_matched(fn):
2016 continue
2017 unmatched_regex.remove(unmatch)
2018 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002019
2020 for collection, pattern, regex, _ in self.bbfile_config_priorities:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05002021 if regex in unmatched_regex:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002022 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Andrew Geissler5a43b432020-06-13 10:46:56 -05002023 collectlog.warning("No bb files in %s matched BBFILE_PATTERN_%s '%s'" % (self.mc if self.mc else 'default',
2024 collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002025
2026 return priorities
2027
2028class ParsingFailure(Exception):
2029 def __init__(self, realexception, recipe):
2030 self.realexception = realexception
2031 self.recipe = recipe
2032 Exception.__init__(self, realexception, recipe)
2033
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002034class Parser(multiprocessing.Process):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002035 def __init__(self, jobs, results, quit, profile):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002036 self.jobs = jobs
2037 self.results = results
2038 self.quit = quit
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002039 multiprocessing.Process.__init__(self)
2040 self.context = bb.utils.get_context().copy()
2041 self.handlers = bb.event.get_class_handlers().copy()
2042 self.profile = profile
Andrew Geissler9aee5002022-03-30 16:27:02 +00002043 self.queue_signals = False
2044 self.signal_received = []
2045 self.signal_threadlock = threading.Lock()
2046
2047 def catch_sig(self, signum, frame):
2048 if self.queue_signals:
2049 self.signal_received.append(signum)
2050 else:
2051 self.handle_sig(signum, frame)
2052
2053 def handle_sig(self, signum, frame):
2054 if signum == signal.SIGTERM:
2055 signal.signal(signal.SIGTERM, signal.SIG_DFL)
2056 os.kill(os.getpid(), signal.SIGTERM)
2057 elif signum == signal.SIGINT:
2058 signal.default_int_handler(signum, frame)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002059
2060 def run(self):
2061
2062 if not self.profile:
2063 self.realrun()
2064 return
2065
2066 try:
2067 import cProfile as profile
2068 except:
2069 import profile
2070 prof = profile.Profile()
2071 try:
2072 profile.Profile.runcall(prof, self.realrun)
2073 finally:
2074 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
2075 prof.dump_stats(logfile)
2076
2077 def realrun(self):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002078 # Signal handling here is hard. We must not terminate any process or thread holding the write
2079 # lock for the event stream as it will not be released, ever, and things will hang.
2080 # Python handles signals in the main thread/process but they can be raised from any thread and
2081 # we want to defer processing of any SIGTERM/SIGINT signal until we're outside the critical section
2082 # and don't hold the lock (see server/process.py). We therefore always catch the signals (so any
2083 # new thread should also do so) and we defer handling but we handle with the local thread lock
2084 # held (a threading lock, not a multiprocessing one) so that no other thread in the process
2085 # can be in the critical section.
2086 signal.signal(signal.SIGTERM, self.catch_sig)
2087 signal.signal(signal.SIGHUP, signal.SIG_DFL)
2088 signal.signal(signal.SIGINT, self.catch_sig)
2089 bb.utils.set_process_name(multiprocessing.current_process().name)
2090 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2091 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002092
2093 pending = []
Andrew Geissler9aee5002022-03-30 16:27:02 +00002094 try:
2095 while True:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002096 try:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002097 self.quit.get_nowait()
2098 except queue.Empty:
2099 pass
2100 else:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002101 break
Andrew Geissler9aee5002022-03-30 16:27:02 +00002102
2103 if pending:
2104 result = pending.pop()
2105 else:
2106 try:
2107 job = self.jobs.pop()
2108 except IndexError:
2109 break
2110 result = self.parse(*job)
2111 # Clear the siggen cache after parsing to control memory usage, its huge
2112 bb.parse.siggen.postparsing_clean_cache()
2113 try:
2114 self.results.put(result, timeout=0.25)
2115 except queue.Full:
2116 pending.append(result)
2117 finally:
2118 self.results.close()
2119 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002120
Andrew Geissler5a43b432020-06-13 10:46:56 -05002121 def parse(self, mc, cache, filename, appends):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002122 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002123 origfilter = bb.event.LogHandler.filter
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002124 # Record the filename we're parsing into any events generated
2125 def parse_filter(self, record):
2126 record.taskpid = bb.event.worker_pid
2127 record.fn = filename
2128 return True
2129
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002130 # Reset our environment and handlers to the original settings
2131 bb.utils.set_context(self.context.copy())
2132 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002133 bb.event.LogHandler.filter = parse_filter
2134
Andrew Geissler5a43b432020-06-13 10:46:56 -05002135 return True, mc, cache.parse(filename, appends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002136 except Exception as exc:
2137 tb = sys.exc_info()[2]
2138 exc.recipe = filename
2139 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002140 return True, None, exc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002141 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
2142 # and for example a worker thread doesn't just exit on its own in response to
2143 # a SystemExit event for example.
2144 except BaseException as exc:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002145 return True, None, ParsingFailure(exc, filename)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002146 finally:
2147 bb.event.LogHandler.filter = origfilter
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002148
2149class CookerParser(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002150 def __init__(self, cooker, mcfilelist, masked):
2151 self.mcfilelist = mcfilelist
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002152 self.cooker = cooker
2153 self.cfgdata = cooker.data
2154 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002155 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002156
2157 # Accounting statistics
2158 self.parsed = 0
2159 self.cached = 0
2160 self.error = 0
2161 self.masked = masked
2162
2163 self.skipped = 0
2164 self.virtuals = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002165
2166 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002167 self.process_names = []
2168
Andrew Geissler5a43b432020-06-13 10:46:56 -05002169 self.bb_caches = bb.cache.MulticonfigCache(self.cfgbuilder, self.cfghash, cooker.caches_array)
2170 self.fromcache = set()
2171 self.willparse = set()
2172 for mc in self.cooker.multiconfigs:
2173 for filename in self.mcfilelist[mc]:
2174 appends = self.cooker.collections[mc].get_file_appends(filename)
2175 if not self.bb_caches[mc].cacheValid(filename, appends):
2176 self.willparse.add((mc, self.bb_caches[mc], filename, appends))
2177 else:
2178 self.fromcache.add((mc, self.bb_caches[mc], filename, appends))
2179
2180 self.total = len(self.fromcache) + len(self.willparse)
2181 self.toparse = len(self.willparse)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002182 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002183
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002184 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Andrew Geissler5a43b432020-06-13 10:46:56 -05002185 multiprocessing.cpu_count()), self.toparse)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002186
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002187 self.start()
2188 self.haveshutdown = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002189 self.syncthread = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002190
2191 def start(self):
2192 self.results = self.load_cached()
2193 self.processes = []
2194 if self.toparse:
2195 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002196
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002197 self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002198 self.result_queue = multiprocessing.Queue()
Brad Bishop19323692019-04-05 15:28:33 -04002199
2200 def chunkify(lst,n):
2201 return [lst[i::n] for i in range(n)]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002202 self.jobs = chunkify(list(self.willparse), self.num_processes)
Brad Bishop19323692019-04-05 15:28:33 -04002203
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002204 for i in range(0, self.num_processes):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002205 parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, self.cooker.configuration.profile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002206 parser.start()
2207 self.process_names.append(parser.name)
2208 self.processes.append(parser)
2209
2210 self.results = itertools.chain(self.results, self.parse_generator())
2211
Andrew Geissler9aee5002022-03-30 16:27:02 +00002212 def shutdown(self, clean=True):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002213 if not self.toparse:
2214 return
2215 if self.haveshutdown:
2216 return
2217 self.haveshutdown = True
2218
2219 if clean:
2220 event = bb.event.ParseCompleted(self.cached, self.parsed,
2221 self.skipped, self.masked,
2222 self.virtuals, self.error,
2223 self.total)
2224
2225 bb.event.fire(event, self.cfgdata)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002226 else:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002227 bb.error("Parsing halted due to errors, see error messages above")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002228
2229 for process in self.processes:
2230 self.parser_quit.put(None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002231
Brad Bishop08902b02019-08-20 09:16:51 -04002232 # Cleanup the queue before call process.join(), otherwise there might be
2233 # deadlocks.
2234 while True:
2235 try:
2236 self.result_queue.get(timeout=0.25)
2237 except queue.Empty:
2238 break
2239
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002240 for process in self.processes:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002241 process.join(0.5)
2242
2243 for process in self.processes:
2244 if process.exitcode is None:
2245 os.kill(process.pid, signal.SIGINT)
2246
2247 for process in self.processes:
2248 process.join(0.5)
2249
2250 for process in self.processes:
2251 if process.exitcode is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002252 process.terminate()
Andrew Geissler9aee5002022-03-30 16:27:02 +00002253
2254 for process in self.processes:
2255 process.join()
2256 # Added in 3.7, cleans up zombies
2257 if hasattr(process, "close"):
2258 process.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002259
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002260 self.parser_quit.close()
2261 # Allow data left in the cancel queue to be discarded
2262 self.parser_quit.cancel_join_thread()
2263
Andrew Geissler5a43b432020-06-13 10:46:56 -05002264 def sync_caches():
2265 for c in self.bb_caches.values():
2266 c.sync()
2267
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002268 sync = threading.Thread(target=sync_caches, name="SyncThread")
2269 self.syncthread = sync
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002270 sync.start()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002271 bb.codeparser.parser_cache_savemerge()
2272 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002273 if self.cooker.configuration.profile:
2274 profiles = []
2275 for i in self.process_names:
2276 logfile = "profile-parse-%s.log" % i
2277 if os.path.exists(logfile):
2278 profiles.append(logfile)
2279
2280 pout = "profile-parse.log.processed"
2281 bb.utils.process_profilelog(profiles, pout = pout)
2282 print("Processed parsing statistics saved to %s" % (pout))
2283
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002284 def final_cleanup(self):
2285 if self.syncthread:
2286 self.syncthread.join()
2287
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002288 def load_cached(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002289 for mc, cache, filename, appends in self.fromcache:
2290 cached, infos = cache.load(filename, appends)
2291 yield not cached, mc, infos
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002292
2293 def parse_generator(self):
Andrew Geissler595f6302022-01-24 19:11:47 +00002294 empty = False
2295 while self.processes or not empty:
2296 for process in self.processes.copy():
2297 if not process.is_alive():
2298 process.join()
2299 self.processes.remove(process)
2300
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002301 if self.parsed >= self.toparse:
2302 break
2303
2304 try:
2305 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002306 except queue.Empty:
Andrew Geissler595f6302022-01-24 19:11:47 +00002307 empty = True
Andrew Geissler9aee5002022-03-30 16:27:02 +00002308 yield None, None, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002309 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002310 empty = False
Andrew Geissler9aee5002022-03-30 16:27:02 +00002311 yield result
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002312
Andrew Geissler595f6302022-01-24 19:11:47 +00002313 if not (self.parsed >= self.toparse):
2314 raise bb.parse.ParseError("Not all recipes parsed, parser thread killed/died? Exiting.", None)
2315
2316
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002317 def parse_next(self):
2318 result = []
2319 parsed = None
2320 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002321 parsed, mc, result = next(self.results)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002322 if isinstance(result, BaseException):
2323 # Turn exceptions back into exceptions
2324 raise result
2325 if parsed is None:
2326 # Timeout, loop back through the main loop
2327 return True
2328
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002329 except StopIteration:
2330 self.shutdown()
2331 return False
2332 except bb.BBHandledException as exc:
2333 self.error += 1
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002334 logger.debug('Failed to parse recipe: %s' % exc.recipe)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002335 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002336 return False
2337 except ParsingFailure as exc:
2338 self.error += 1
2339 logger.error('Unable to parse %s: %s' %
2340 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002341 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002342 return False
2343 except bb.parse.ParseError as exc:
2344 self.error += 1
2345 logger.error(str(exc))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002346 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002347 return False
2348 except bb.data_smart.ExpansionError as exc:
2349 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002350 bbdir = os.path.dirname(__file__) + os.sep
2351 etype, value, _ = sys.exc_info()
2352 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2353 logger.error('ExpansionError during parsing %s', value.recipe,
2354 exc_info=(etype, value, tb))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002355 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002356 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002357 except Exception as exc:
2358 self.error += 1
2359 etype, value, tb = sys.exc_info()
2360 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002361 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002362 exc_info=(etype, value, exc.traceback))
2363 else:
2364 # Most likely, an exception occurred during raising an exception
2365 import traceback
2366 logger.error('Exception during parse: %s' % traceback.format_exc())
Andrew Geissler9aee5002022-03-30 16:27:02 +00002367 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002368 return False
2369
2370 self.current += 1
2371 self.virtuals += len(result)
2372 if parsed:
2373 self.parsed += 1
2374 if self.parsed % self.progress_chunk == 0:
2375 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2376 self.cfgdata)
2377 else:
2378 self.cached += 1
2379
2380 for virtualfn, info_array in result:
2381 if info_array[0].skipped:
2382 self.skipped += 1
2383 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Andrew Geissler5a43b432020-06-13 10:46:56 -05002384 self.bb_caches[mc].add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002385 parsed=parsed, watcher = self.cooker.add_filewatch)
2386 return True
2387
2388 def reparse(self, filename):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002389 to_reparse = set()
2390 for mc in self.cooker.multiconfigs:
2391 to_reparse.add((mc, filename, self.cooker.collections[mc].get_file_appends(filename)))
2392
2393 for mc, filename, appends in to_reparse:
2394 infos = self.bb_caches[mc].parse(filename, appends)
2395 for vfn, info_array in infos:
2396 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)