blob: d658db9bd85123eac2db8c8401e6f84995d1b836 [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
Patrick Williamsc0f7c042017-02-23 20:41:17 -060025import json
26import pickle
27import codecs
Brad Bishop08902b02019-08-20 09:16:51 -040028import hashserv
Patrick Williamsc124f4f2015-09-15 14:41:29 -050029
30logger = logging.getLogger("BitBake")
31collectlog = logging.getLogger("BitBake.Collection")
32buildlog = logging.getLogger("BitBake.Build")
33parselog = logging.getLogger("BitBake.Parsing")
34providerlog = logging.getLogger("BitBake.Provider")
35
36class NoSpecificMatch(bb.BBHandledException):
37 """
38 Exception raised when no or multiple file matches are found
39 """
40
41class NothingToBuild(Exception):
42 """
43 Exception raised when there is nothing to build
44 """
45
46class CollectionError(bb.BBHandledException):
47 """
48 Exception raised when layer configuration is incorrect
49 """
50
51class state:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060052 initial, parsing, running, shutdown, forceshutdown, stopped, error = list(range(7))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050053
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050054 @classmethod
55 def get_name(cls, code):
56 for name in dir(cls):
57 value = getattr(cls, name)
58 if type(value) == type(cls.initial) and value == code:
59 return name
60 raise ValueError("Invalid status code: %s" % code)
61
Patrick Williamsc124f4f2015-09-15 14:41:29 -050062
63class SkippedPackage:
64 def __init__(self, info = None, reason = None):
65 self.pn = None
66 self.skipreason = None
67 self.provides = None
68 self.rprovides = None
69
70 if info:
71 self.pn = info.pn
72 self.skipreason = info.skipreason
73 self.provides = info.provides
Andrew Geisslerd1e89492021-02-12 15:35:20 -060074 self.rprovides = info.packages + info.rprovides
75 for package in info.packages:
76 self.rprovides += info.rprovides_pkg[package]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050077 elif reason:
78 self.skipreason = reason
79
80
81class CookerFeatures(object):
Andrew Geissler517393d2023-01-13 08:55:19 -060082 _feature_list = [HOB_EXTRA_CACHES, BASEDATASTORE_TRACKING, SEND_SANITYEVENTS, RECIPE_SIGGEN_INFO] = list(range(4))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050083
84 def __init__(self):
85 self._features=set()
86
87 def setFeature(self, f):
88 # validate we got a request for a feature we support
89 if f not in CookerFeatures._feature_list:
90 return
91 self._features.add(f)
92
93 def __contains__(self, f):
94 return f in self._features
95
96 def __iter__(self):
97 return self._features.__iter__()
98
Patrick Williamsc0f7c042017-02-23 20:41:17 -060099 def __next__(self):
100 return next(self._features)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500101
102
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600103class EventWriter:
104 def __init__(self, cooker, eventfile):
105 self.file_inited = None
106 self.cooker = cooker
107 self.eventfile = eventfile
108 self.event_queue = []
109
110 def write_event(self, event):
111 with open(self.eventfile, "a") as f:
112 try:
113 str_event = codecs.encode(pickle.dumps(event), 'base64').decode('utf-8')
114 f.write("%s\n" % json.dumps({"class": event.__module__ + "." + event.__class__.__name__,
115 "vars": str_event}))
116 except Exception as err:
117 import traceback
118 print(err, traceback.format_exc())
119
120 def send(self, event):
121 if self.file_inited:
122 # we have the file, just write the event
123 self.write_event(event)
124 else:
125 # init on bb.event.BuildStarted
126 name = "%s.%s" % (event.__module__, event.__class__.__name__)
127 if name in ("bb.event.BuildStarted", "bb.cooker.CookerExit"):
128 with open(self.eventfile, "w") as f:
129 f.write("%s\n" % json.dumps({ "allvariables" : self.cooker.getAllKeysWithFlags(["doc", "func"])}))
130
131 self.file_inited = True
132
133 # write pending events
134 for evt in self.event_queue:
135 self.write_event(evt)
136
137 # also write the current event
138 self.write_event(event)
139 else:
140 # queue all events until the file is inited
141 self.event_queue.append(event)
142
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500143#============================================================================#
144# BBCooker
145#============================================================================#
146class BBCooker:
147 """
148 Manages one bitbake build run
149 """
150
Andrew Geissler517393d2023-01-13 08:55:19 -0600151 def __init__(self, featureSet=None, server=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600152 self.recipecaches = None
Andrew Geissler028142b2023-05-05 11:29:21 -0500153 self.baseconfig_valid = False
154 self.parsecache_valid = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500155 self.eventlog = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500156 self.skiplist = {}
157 self.featureset = CookerFeatures()
158 if featureSet:
159 for f in featureSet:
160 self.featureset.setFeature(f)
161
Patrick Williams45852732022-04-02 08:58:32 -0500162 self.orig_syspath = sys.path.copy()
163 self.orig_sysmodules = [*sys.modules]
164
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500165 self.configuration = bb.cookerdata.CookerConfiguration()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500166
Andrew Geissler517393d2023-01-13 08:55:19 -0600167 self.process_server = server
168 self.idleCallBackRegister = None
169 self.waitIdle = None
170 if server:
171 self.idleCallBackRegister = server.register_idle_function
172 self.waitIdle = server.wait_for_idle
Andrew Geissler635e0e42020-08-21 15:58:33 -0500173
Brad Bishopf058f492019-01-28 23:50:33 -0500174 bb.debug(1, "BBCooker starting %s" % time.time())
Brad Bishopf058f492019-01-28 23:50:33 -0500175
Andrew Geissler220dafd2023-10-04 10:18:08 -0500176 self.configwatched = {}
177 self.parsewatched = {}
Brad Bishopf058f492019-01-28 23:50:33 -0500178
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500179 # If being called by something like tinfoil, we need to clean cached data
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500180 # which may now be invalid
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500181 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500182 bb.parse.BBHandler.cached_statements = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500183
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500184 self.ui_cmdline = None
Brad Bishop08902b02019-08-20 09:16:51 -0400185 self.hashserv = None
Brad Bishopa34c0302019-09-23 22:34:48 -0400186 self.hashservaddr = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500187
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500188 # TOSTOP must not be set or our children will hang when they output
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600189 try:
190 fd = sys.stdout.fileno()
191 if os.isatty(fd):
192 import termios
193 tcattr = termios.tcgetattr(fd)
194 if tcattr[3] & termios.TOSTOP:
195 buildlog.info("The terminal had the TOSTOP bit set, clearing...")
196 tcattr[3] = tcattr[3] & ~termios.TOSTOP
197 termios.tcsetattr(fd, termios.TCSANOW, tcattr)
198 except UnsupportedOperation:
199 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500200
Andrew Geissler517393d2023-01-13 08:55:19 -0600201 self.command = bb.command.Command(self, self.process_server)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500202 self.state = state.initial
203
204 self.parser = None
205
206 signal.signal(signal.SIGTERM, self.sigterm_exception)
207 # Let SIGHUP exit as SIGTERM
208 signal.signal(signal.SIGHUP, self.sigterm_exception)
209
Brad Bishopf058f492019-01-28 23:50:33 -0500210 bb.debug(1, "BBCooker startup complete %s" % time.time())
Andrew Geissler517393d2023-01-13 08:55:19 -0600211
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500212 def init_configdata(self):
213 if not hasattr(self, "data"):
214 self.initConfigurationData()
215 bb.debug(1, "BBCooker parsed base configuration %s" % time.time())
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500216 self.handlePRServ()
217
Andrew Geissler028142b2023-05-05 11:29:21 -0500218 def _baseconfig_set(self, value):
219 if value and not self.baseconfig_valid:
220 bb.server.process.serverlog("Base config valid")
221 elif not value and self.baseconfig_valid:
222 bb.server.process.serverlog("Base config invalidated")
223 self.baseconfig_valid = value
224
225 def _parsecache_set(self, value):
226 if value and not self.parsecache_valid:
227 bb.server.process.serverlog("Parse cache valid")
228 elif not value and self.parsecache_valid:
229 bb.server.process.serverlog("Parse cache invalidated")
230 self.parsecache_valid = value
231
Andrew Geissler220dafd2023-10-04 10:18:08 -0500232 def add_filewatch(self, deps, configwatcher=False):
233 if configwatcher:
234 watcher = self.configwatched
235 else:
236 watcher = self.parsewatched
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500237
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500238 for i in deps:
Andrew Geissler220dafd2023-10-04 10:18:08 -0500239 f = i[0]
240 mtime = i[1]
241 watcher[f] = mtime
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600242
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500243 def sigterm_exception(self, signum, stackframe):
244 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500245 bb.warn("Cooker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500246 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500247 bb.warn("Cooker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500248 self.state = state.forceshutdown
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600249 bb.event._should_exit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500250
251 def setFeatures(self, features):
252 # we only accept a new feature set if we're in state initial, so we can reset without problems
253 if not self.state in [state.initial, state.shutdown, state.forceshutdown, state.stopped, state.error]:
254 raise Exception("Illegal state for feature set change")
255 original_featureset = list(self.featureset)
256 for feature in features:
257 self.featureset.setFeature(feature)
258 bb.debug(1, "Features set %s (was %s)" % (original_featureset, list(self.featureset)))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500259 if (original_featureset != list(self.featureset)) and self.state != state.error and hasattr(self, "data"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500260 self.reset()
261
262 def initConfigurationData(self):
263
264 self.state = state.initial
265 self.caches_array = []
266
Patrick Williams45852732022-04-02 08:58:32 -0500267 sys.path = self.orig_syspath.copy()
268 for mod in [*sys.modules]:
269 if mod not in self.orig_sysmodules:
270 del sys.modules[mod]
271
Andrew Geissler220dafd2023-10-04 10:18:08 -0500272 self.configwatched = {}
Patrick Williamsde0582f2022-04-08 10:23:27 -0500273
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500274 # Need to preserve BB_CONSOLELOG over resets
275 consolelog = None
276 if hasattr(self, "data"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500277 consolelog = self.data.getVar("BB_CONSOLELOG")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500278
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500279 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
280 self.enableDataTracking()
281
Andrew Geissler517393d2023-01-13 08:55:19 -0600282 caches_name_array = ['bb.cache:CoreRecipeInfo']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283 # We hardcode all known cache types in a single place, here.
284 if CookerFeatures.HOB_EXTRA_CACHES in self.featureset:
Andrew Geissler517393d2023-01-13 08:55:19 -0600285 caches_name_array.append("bb.cache_extra:HobRecipeInfo")
286 if CookerFeatures.RECIPE_SIGGEN_INFO in self.featureset:
287 caches_name_array.append("bb.cache:SiggenRecipeInfo")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500288
289 # At least CoreRecipeInfo will be loaded, so caches_array will never be empty!
290 # This is the entry point, no further check needed!
291 for var in caches_name_array:
292 try:
293 module_name, cache_name = var.split(':')
294 module = __import__(module_name, fromlist=(cache_name,))
295 self.caches_array.append(getattr(module, cache_name))
296 except ImportError as exc:
297 logger.critical("Unable to import extra RecipeInfo '%s' from '%s': %s" % (cache_name, module_name, exc))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500298 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500299
300 self.databuilder = bb.cookerdata.CookerDataBuilder(self.configuration, False)
301 self.databuilder.parseBaseConfiguration()
302 self.data = self.databuilder.data
303 self.data_hash = self.databuilder.data_hash
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500304 self.extraconfigdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500305
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600306 eventlog = self.data.getVar("BB_DEFAULT_EVENTLOG")
307 if not self.configuration.writeeventlog and eventlog:
308 self.setupEventLog(eventlog)
309
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500310 if consolelog:
311 self.data.setVar("BB_CONSOLELOG", consolelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500312
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500313 self.data.setVar('BB_CMDLINE', self.ui_cmdline)
314
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500315 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
316 self.disableDataTracking()
317
Brad Bishop15ae2502019-06-18 21:44:24 -0400318 for mc in self.databuilder.mcdata.values():
Andrew Geissler220dafd2023-10-04 10:18:08 -0500319 self.add_filewatch(mc.getVar("__base_depends", False), configwatcher=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500320
Andrew Geissler028142b2023-05-05 11:29:21 -0500321 self._baseconfig_set(True)
322 self._parsecache_set(False)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500323
324 def handlePRServ(self):
325 # Setup a PR Server based on the new configuration
326 try:
327 self.prhost = prserv.serv.auto_start(self.data)
328 except prserv.serv.PRServiceConfigError as e:
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500329 bb.fatal("Unable to start PR Server, exiting, check the bitbake-cookerdaemon.log")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500330
Brad Bishopa34c0302019-09-23 22:34:48 -0400331 if self.data.getVar("BB_HASHSERVE") == "auto":
332 # Create a new hash server bound to a unix domain socket
Brad Bishop08902b02019-08-20 09:16:51 -0400333 if not self.hashserv:
334 dbfile = (self.data.getVar("PERSISTENT_DIR") or self.data.getVar("CACHE")) + "/hashserv.db"
Andrew Geissler595f6302022-01-24 19:11:47 +0000335 upstream = self.data.getVar("BB_HASHSERVE_UPSTREAM") or None
336 if upstream:
337 import socket
338 try:
339 sock = socket.create_connection(upstream.split(":"), 5)
340 sock.close()
341 except socket.error as e:
Andrew Geissler87f5cff2022-09-30 13:13:31 -0500342 bb.warn("BB_HASHSERVE_UPSTREAM is not valid, unable to connect hash equivalence server at '%s': %s"
Andrew Geissler595f6302022-01-24 19:11:47 +0000343 % (upstream, repr(e)))
344
Brad Bishopa34c0302019-09-23 22:34:48 -0400345 self.hashservaddr = "unix://%s/hashserve.sock" % self.data.getVar("TOPDIR")
Andrew Geissler5199d832021-09-24 16:47:35 -0500346 self.hashserv = hashserv.create_server(
347 self.hashservaddr,
348 dbfile,
349 sync=False,
Andrew Geissler595f6302022-01-24 19:11:47 +0000350 upstream=upstream,
Andrew Geissler5199d832021-09-24 16:47:35 -0500351 )
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600352 self.hashserv.serve_as_process(log_level=logging.WARNING)
Brad Bishop08902b02019-08-20 09:16:51 -0400353 for mc in self.databuilder.mcdata:
Andrew Geissler517393d2023-01-13 08:55:19 -0600354 self.databuilder.mcorigdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishopa34c0302019-09-23 22:34:48 -0400355 self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400356
357 bb.parse.init_parser(self.data)
358
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500359 def enableDataTracking(self):
360 self.configuration.tracking = True
361 if hasattr(self, "data"):
362 self.data.enableTracking()
363
364 def disableDataTracking(self):
365 self.configuration.tracking = False
366 if hasattr(self, "data"):
367 self.data.disableTracking()
368
Andrew Geissler220dafd2023-10-04 10:18:08 -0500369 def revalidateCaches(self):
370 bb.parse.clear_cache()
371
372 clean = True
373 for f in self.configwatched:
374 if not bb.parse.check_mtime(f, self.configwatched[f]):
375 bb.server.process.serverlog("Found %s changed, invalid cache" % f)
376 self._baseconfig_set(False)
377 self._parsecache_set(False)
378 clean = False
379 break
380
381 if clean:
382 for f in self.parsewatched:
383 if not bb.parse.check_mtime(f, self.parsewatched[f]):
384 bb.server.process.serverlog("Found %s changed, invalid cache" % f)
385 self._parsecache_set(False)
386 clean = False
387 break
388
389 if not clean:
390 bb.parse.BBHandler.cached_statements = {}
391
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500392 def parseConfiguration(self):
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600393 self.updateCacheSync()
394
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500395 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500396 nice = self.data.getVar("BB_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500397 if nice:
398 curnice = os.nice(0)
399 nice = int(nice) - curnice
400 buildlog.verbose("Renice to %s " % os.nice(nice))
401
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600402 if self.recipecaches:
403 del self.recipecaches
404 self.multiconfigs = self.databuilder.mcdata.keys()
405 self.recipecaches = {}
406 for mc in self.multiconfigs:
407 self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500408
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500409 self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
Patrick Williamse760df82023-05-26 11:10:49 -0500410 self.collections = {}
411 for mc in self.multiconfigs:
412 self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500413
Andrew Geissler028142b2023-05-05 11:29:21 -0500414 self._parsecache_set(False)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500415
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600416 def setupEventLog(self, eventlog):
417 if self.eventlog and self.eventlog[0] != eventlog:
418 bb.event.unregister_UIHhandler(self.eventlog[1])
419 if not self.eventlog or self.eventlog[0] != eventlog:
420 # we log all events to a file if so directed
421 # register the log file writer as UI Handler
422 if not os.path.exists(os.path.dirname(eventlog)):
423 bb.utils.mkdirhier(os.path.dirname(eventlog))
424 writer = EventWriter(self, eventlog)
425 EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
426 self.eventlog = (eventlog, bb.event.register_UIHhandler(EventLogWriteHandler(writer)))
427
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500428 def updateConfigOpts(self, options, environment, cmdline):
429 self.ui_cmdline = cmdline
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500430 clean = True
431 for o in options:
432 if o in ['prefile', 'postfile']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500433 # Only these options may require a reparse
434 try:
435 if getattr(self.configuration, o) == options[o]:
436 # Value is the same, no need to mark dirty
437 continue
438 except AttributeError:
439 pass
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600440 logger.debug("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500441 print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500442 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500443 if hasattr(self.configuration, o):
444 setattr(self.configuration, o, options[o])
445
446 if self.configuration.writeeventlog:
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600447 self.setupEventLog(self.configuration.writeeventlog)
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500448
449 bb.msg.loggerDefaultLogLevel = self.configuration.default_loglevel
450 bb.msg.loggerDefaultDomains = self.configuration.debug_domains
451
452 if hasattr(self, "data"):
453 origenv = bb.data.init()
454 for k in environment:
455 origenv.setVar(k, environment[k])
456 self.data.setVar("BB_ORIGENV", origenv)
457
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500458 for k in bb.utils.approved_variables():
459 if k in environment and k not in self.configuration.env:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600460 logger.debug("Updating new environment variable %s to %s" % (k, environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500461 self.configuration.env[k] = environment[k]
462 clean = False
463 if k in self.configuration.env and k not in environment:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600464 logger.debug("Updating environment variable %s (deleted)" % (k))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500465 del self.configuration.env[k]
466 clean = False
467 if k not in self.configuration.env and k not in environment:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500468 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500469 if environment[k] != self.configuration.env[k]:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600470 logger.debug("Updating environment variable %s from %s to %s" % (k, self.configuration.env[k], environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471 self.configuration.env[k] = environment[k]
472 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500473
474 # Now update all the variables not in the datastore to match
475 self.configuration.env = environment
476
Andrew Geissler220dafd2023-10-04 10:18:08 -0500477 self.revalidateCaches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500478 if not clean:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600479 logger.debug("Base environment change, triggering reparse")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500480 self.reset()
481
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500482 def showVersions(self):
483
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500484 (latest_versions, preferred_versions, required) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500485
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500486 logger.plain("%-35s %25s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version", "Required Version")
487 logger.plain("%-35s %25s %25s %25s\n", "===========", "==============", "=================", "================")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500489 for p in sorted(self.recipecaches[''].pkg_pn):
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500490 preferred = preferred_versions[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491 latest = latest_versions[p]
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500492 requiredstr = ""
493 preferredstr = ""
494 if required[p]:
495 if preferred[0] is not None:
496 requiredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
497 else:
498 bb.fatal("REQUIRED_VERSION of package %s not available" % p)
499 else:
500 preferredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500501
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500502 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
503
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500504 if preferred == latest:
505 preferredstr = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500506
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500507 logger.plain("%-35s %25s %25s %25s", p, lateststr, preferredstr, requiredstr)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500508
509 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
510 """
511 Show the outer or per-recipe environment
512 """
513 fn = None
514 envdata = None
Brad Bishop15ae2502019-06-18 21:44:24 -0400515 mc = ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500516 if not pkgs_to_build:
517 pkgs_to_build = []
518
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500519 orig_tracking = self.configuration.tracking
520 if not orig_tracking:
521 self.enableDataTracking()
522 self.reset()
Andrew Geissler9aee5002022-03-30 16:27:02 +0000523 # reset() resets to the UI requested value so we have to redo this
524 self.enableDataTracking()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500525
Brad Bishop15ae2502019-06-18 21:44:24 -0400526 def mc_base(p):
527 if p.startswith('mc:'):
528 s = p.split(':')
529 if len(s) == 2:
530 return s[1]
531 return None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500532
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500533 if buildfile:
534 # Parse the configuration here. We need to do it explicitly here since
535 # this showEnvironment() code path doesn't use the cache
536 self.parseConfiguration()
537
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600538 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500539 fn = self.matchFile(fn, mc)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600540 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500541 elif len(pkgs_to_build) == 1:
Brad Bishop15ae2502019-06-18 21:44:24 -0400542 mc = mc_base(pkgs_to_build[0])
543 if not mc:
544 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
545 if pkgs_to_build[0] in set(ignore.split()):
546 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500547
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000548 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.halt, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500549
Brad Bishop15ae2502019-06-18 21:44:24 -0400550 mc = runlist[0][0]
551 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500552
553 if fn:
554 try:
Patrick Williamse760df82023-05-26 11:10:49 -0500555 layername = self.collections[mc].calc_bbfile_priority(fn)[2]
556 envdata = self.databuilder.parseRecipe(fn, self.collections[mc].get_file_appends(fn), layername)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500557 except Exception as e:
558 parselog.exception("Unable to read %s", fn)
559 raise
Brad Bishop15ae2502019-06-18 21:44:24 -0400560 else:
561 if not mc in self.databuilder.mcdata:
Andrew Geissler5082cc72023-09-11 08:41:39 -0400562 bb.fatal('No multiconfig named "%s" found' % mc)
Brad Bishop15ae2502019-06-18 21:44:24 -0400563 envdata = self.databuilder.mcdata[mc]
564 data.expandKeys(envdata)
565 parse.ast.runAnonFuncs(envdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566
567 # Display history
568 with closing(StringIO()) as env:
569 self.data.inchistory.emit(env)
570 logger.plain(env.getvalue())
571
572 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573 with closing(StringIO()) as env:
574 data.emit_env(env, envdata, True)
575 logger.plain(env.getvalue())
576
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000577 # emit the metadata which isn't valid shell
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500578 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600579 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500580 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500581
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500582 if not orig_tracking:
583 self.disableDataTracking()
584 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500585
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000586 def buildTaskData(self, pkgs_to_build, task, halt, allowincomplete=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500587 """
588 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
589 """
590 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
591
592 # A task of None means use the default task
593 if task is None:
594 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500595 if not task.startswith("do_"):
596 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500598 targetlist = self.checkPackages(pkgs_to_build, task)
599 fulltargetlist = []
600 defaulttask_implicit = ''
601 defaulttask_explicit = False
602 wildcard = False
603
604 # Wild card expansion:
Brad Bishop15ae2502019-06-18 21:44:24 -0400605 # Replace string such as "mc:*:bash"
606 # into "mc:A:bash mc:B:bash bash"
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500607 for k in targetlist:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600608 if k.startswith("mc:") and k.count(':') >= 2:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500609 if wildcard:
610 bb.fatal('multiconfig conflict')
611 if k.split(":")[1] == "*":
612 wildcard = True
613 for mc in self.multiconfigs:
614 if mc:
615 fulltargetlist.append(k.replace('*', mc))
616 # implicit default task
617 else:
618 defaulttask_implicit = k.split(":")[2]
619 else:
620 fulltargetlist.append(k)
621 else:
622 defaulttask_explicit = True
623 fulltargetlist.append(k)
624
625 if not defaulttask_explicit and defaulttask_implicit != '':
626 fulltargetlist.append(defaulttask_implicit)
627
628 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600629 taskdata = {}
630 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600632 for mc in self.multiconfigs:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000633 taskdata[mc] = bb.taskdata.TaskData(halt, skiplist=self.skiplist, allowincomplete=allowincomplete)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600634 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600635 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500636
637 current = 0
638 runlist = []
639 for k in fulltargetlist:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500640 origk = k
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600641 mc = ""
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600642 if k.startswith("mc:") and k.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600643 mc = k.split(":")[1]
644 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500645 ktask = task
646 if ":do_" in k:
647 k2 = k.split(":do_")
648 k = k2[0]
649 ktask = k2[1]
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500650
651 if mc not in self.multiconfigs:
652 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named %s" % (origk, mc))
653
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600654 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500655 current += 1
656 if not ktask.startswith("do_"):
657 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600658 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
659 # e.g. in ASSUME_PROVIDED
660 continue
661 fn = taskdata[mc].build_targets[k][0]
662 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600664
Brad Bishop15ae2502019-06-18 21:44:24 -0400665 havemc = False
666 for mc in self.multiconfigs:
667 if taskdata[mc].get_mcdepends():
668 havemc = True
Brad Bishopf058f492019-01-28 23:50:33 -0500669
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800670 # No need to do check providers if there are no mcdeps or not an mc build
Brad Bishop15ae2502019-06-18 21:44:24 -0400671 if havemc or len(self.multiconfigs) > 1:
Andrew Geissler99467da2019-02-25 18:54:23 -0600672 seen = set()
673 new = True
674 # Make sure we can provide the multiconfig dependency
675 while new:
676 mcdeps = set()
677 # Add unresolved first, so we can get multiconfig indirect dependencies on time
678 for mc in self.multiconfigs:
679 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
680 mcdeps |= set(taskdata[mc].get_mcdepends())
681 new = False
Patrick Williams03907ee2022-05-01 06:28:52 -0500682 for k in mcdeps:
683 if k in seen:
684 continue
685 l = k.split(':')
686 depmc = l[2]
687 if depmc not in self.multiconfigs:
688 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named configuration %s" % (k,depmc))
689 else:
690 logger.debug("Adding providers for multiconfig dependency %s" % l[3])
691 taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
692 seen.add(k)
693 new = True
Brad Bishopf058f492019-01-28 23:50:33 -0500694
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600695 for mc in self.multiconfigs:
696 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
697
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500698 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600699 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500700
701 def prepareTreeData(self, pkgs_to_build, task):
702 """
703 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
704 """
705
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000706 # We set halt to False here to prevent unbuildable targets raising
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600708 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500709
710 return runlist, taskdata
711
712 ######## WARNING : this function requires cache_extra to be enabled ########
713
714 def generateTaskDepTreeData(self, pkgs_to_build, task):
715 """
716 Create a dependency graph of pkgs_to_build including reverse dependency
717 information.
718 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500719 if not task.startswith("do_"):
720 task = "do_%s" % task
721
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500722 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500724 rq.rqdata.prepare()
725 return self.buildDependTree(rq, taskdata)
726
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600727 @staticmethod
728 def add_mc_prefix(mc, pn):
729 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -0400730 return "mc:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600731 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500732
733 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600734 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500735 depend_tree = {}
736 depend_tree["depends"] = {}
737 depend_tree["tdepends"] = {}
738 depend_tree["pn"] = {}
739 depend_tree["rdepends-pn"] = {}
740 depend_tree["packages"] = {}
741 depend_tree["rdepends-pkg"] = {}
742 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500743 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600744 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500745
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600746 for mc in taskdata:
747 for name, fn in list(taskdata[mc].get_providermap().items()):
748 pn = self.recipecaches[mc].pkg_fn[fn]
749 pn = self.add_mc_prefix(mc, pn)
750 if name != pn:
751 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
752 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500753
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600754 for tid in rq.rqdata.runtaskentries:
755 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
756 pn = self.recipecaches[mc].pkg_fn[taskfn]
757 pn = self.add_mc_prefix(mc, pn)
758 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759 if pn not in depend_tree["pn"]:
760 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600761 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500762 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600763 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500764
765 # if we have extra caches, list all attributes they bring in
766 extra_info = []
767 for cache_class in self.caches_array:
768 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
769 cachefields = getattr(cache_class, 'cachefields', [])
770 extra_info = extra_info + cachefields
771
772 # for all attributes stored, add them to the dependency tree
773 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600774 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775
776
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500777 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
778 if not dotname in depend_tree["tdepends"]:
779 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600780 for dep in rq.rqdata.runtaskentries[tid].depends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800781 (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
782 deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
Andrew Geissler595f6302022-01-24 19:11:47 +0000783 if depmc:
784 depmc = "mc:" + depmc + ":"
785 depend_tree["tdepends"][dotname].append("%s%s.%s" % (depmc, deppn, bb.runqueue.taskname_from_tid(dep)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600786 if taskfn not in seen_fns:
787 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500788 packages = []
789
790 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600791 for dep in taskdata[mc].depids[taskfn]:
792 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500793
794 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600795 for rdep in taskdata[mc].rdepids[taskfn]:
796 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500797
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500799 for package in rdepends:
800 depend_tree["rdepends-pkg"][package] = []
801 for rdepend in rdepends[package]:
802 depend_tree["rdepends-pkg"][package].append(rdepend)
803 packages.append(package)
804
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600805 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500806 for package in rrecs:
807 depend_tree["rrecs-pkg"][package] = []
808 for rdepend in rrecs[package]:
809 depend_tree["rrecs-pkg"][package].append(rdepend)
810 if not package in packages:
811 packages.append(package)
812
813 for package in packages:
814 if package not in depend_tree["packages"]:
815 depend_tree["packages"][package] = {}
816 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600817 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500818 depend_tree["packages"][package]["version"] = version
819
820 return depend_tree
821
822 ######## WARNING : this function requires cache_extra to be enabled ########
823 def generatePkgDepTreeData(self, pkgs_to_build, task):
824 """
825 Create a dependency tree of pkgs_to_build, returning the data.
826 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500827 if not task.startswith("do_"):
828 task = "do_%s" % task
829
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500830 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500831
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600832 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833 depend_tree = {}
834 depend_tree["depends"] = {}
835 depend_tree["pn"] = {}
836 depend_tree["rdepends-pn"] = {}
837 depend_tree["rdepends-pkg"] = {}
838 depend_tree["rrecs-pkg"] = {}
839
840 # if we have extra caches, list all attributes they bring in
841 extra_info = []
842 for cache_class in self.caches_array:
843 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
844 cachefields = getattr(cache_class, 'cachefields', [])
845 extra_info = extra_info + cachefields
846
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600847 tids = []
848 for mc in taskdata:
849 for tid in taskdata[mc].taskentries:
850 tids.append(tid)
851
852 for tid in tids:
853 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
854
855 pn = self.recipecaches[mc].pkg_fn[taskfn]
856 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500857
858 if pn not in depend_tree["pn"]:
859 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600860 depend_tree["pn"][pn]["filename"] = taskfn
861 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500862 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600863 rdepends = self.recipecaches[mc].rundeps[taskfn]
864 rrecs = self.recipecaches[mc].runrecs[taskfn]
865 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500866
867 # for all extra attributes stored, add them to the dependency tree
868 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600869 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500870
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600871 if taskfn not in seen_fns:
872 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500873
874 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500875 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500876 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600877 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
878 fn_provider = taskdata[mc].build_targets[dep][0]
879 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500880 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500881 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600882 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500883 depend_tree["depends"][pn].append(pn_provider)
884
885 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600886 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500887 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600888 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
889 fn_rprovider = taskdata[mc].run_targets[rdep][0]
890 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500891 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600892 pn_rprovider = rdep
893 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500894 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
895
896 depend_tree["rdepends-pkg"].update(rdepends)
897 depend_tree["rrecs-pkg"].update(rrecs)
898
899 return depend_tree
900
901 def generateDepTreeEvent(self, pkgs_to_build, task):
902 """
903 Create a task dependency graph of pkgs_to_build.
904 Generate an event with the result
905 """
906 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
907 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
908
909 def generateDotGraphFiles(self, pkgs_to_build, task):
910 """
911 Create a task dependency graph of pkgs_to_build.
912 Save the result to a set of .dot files.
913 """
914
915 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
916
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500917 with open('pn-buildlist', 'w') as f:
918 for pn in depgraph["pn"]:
919 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500920 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500921
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500922 # Remove old format output files to ensure no confusion with stale data
923 try:
924 os.unlink('pn-depends.dot')
925 except FileNotFoundError:
926 pass
927 try:
928 os.unlink('package-depends.dot')
929 except FileNotFoundError:
930 pass
Brad Bishop79641f22019-09-10 07:20:22 -0400931 try:
932 os.unlink('recipe-depends.dot')
933 except FileNotFoundError:
934 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500935
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500936 with open('task-depends.dot', 'w') as f:
937 f.write("digraph depends {\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400938 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500939 (pn, taskname) = task.rsplit(".", 1)
940 fn = depgraph["pn"][pn]["filename"]
941 version = depgraph["pn"][pn]["version"]
942 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 -0400943 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500944 f.write('"%s" -> "%s"\n' % (task, dep))
945 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500946 logger.info("Task dependencies saved to 'task-depends.dot'")
947
948 def show_appends_with_no_recipes(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500949 appends_without_recipes = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500950 # Determine which bbappends haven't been applied
Andrew Geissler5a43b432020-06-13 10:46:56 -0500951 for mc in self.multiconfigs:
952 # First get list of recipes, including skipped
953 recipefns = list(self.recipecaches[mc].pkg_fn.keys())
954 recipefns.extend(self.skiplist.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500955
Andrew Geissler5a43b432020-06-13 10:46:56 -0500956 # Work out list of bbappends that have been applied
957 applied_appends = []
958 for fn in recipefns:
959 applied_appends.extend(self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500960
Andrew Geissler5a43b432020-06-13 10:46:56 -0500961 appends_without_recipes[mc] = []
962 for _, appendfn in self.collections[mc].bbappends:
963 if not appendfn in applied_appends:
964 appends_without_recipes[mc].append(appendfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500965
Andrew Geissler5a43b432020-06-13 10:46:56 -0500966 msgs = []
967 for mc in sorted(appends_without_recipes.keys()):
968 if appends_without_recipes[mc]:
969 msgs.append('No recipes in %s available for:\n %s' % (mc if mc else 'default',
970 '\n '.join(appends_without_recipes[mc])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500971
Andrew Geissler5a43b432020-06-13 10:46:56 -0500972 if msgs:
973 msg = "\n".join(msgs)
974 warn_only = self.databuilder.mcdata[mc].getVar("BB_DANGLINGAPPENDS_WARNONLY", \
975 False) or "no"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500976 if warn_only.lower() in ("1", "yes", "true"):
977 bb.warn(msg)
978 else:
979 bb.fatal(msg)
980
981 def handlePrefProviders(self):
982
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600983 for mc in self.multiconfigs:
984 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600985 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500986
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600987 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500988 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600989 try:
990 (providee, provider) = p.split(':')
991 except:
992 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
993 continue
994 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
995 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
996 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500997
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500998 def findConfigFilePath(self, configfile):
999 """
1000 Find the location on disk of configfile and if it exists and was parsed by BitBake
1001 emit the ConfigFilePathFound event with the path to the file.
1002 """
1003 path = bb.cookerdata.findConfigFile(configfile, self.data)
1004 if not path:
1005 return
1006
1007 # Generate a list of parsed configuration files by searching the files
1008 # listed in the __depends and __base_depends variables with a .conf suffix.
1009 conffiles = []
1010 dep_files = self.data.getVar('__base_depends', False) or []
1011 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
1012
1013 for f in dep_files:
1014 if f[0].endswith(".conf"):
1015 conffiles.append(f[0])
1016
1017 _, conf, conffile = path.rpartition("conf/")
1018 match = os.path.join(conf, conffile)
1019 # Try and find matches for conf/conffilename.conf as we don't always
1020 # have the full path to the file.
1021 for cfg in conffiles:
1022 if cfg.endswith(match):
1023 bb.event.fire(bb.event.ConfigFilePathFound(path),
1024 self.data)
1025 break
1026
1027 def findFilesMatchingInDir(self, filepattern, directory):
1028 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001029 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001030 'directory' in each BBPATH. i.e. to find all rootfs package classes available
1031 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
1032 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001033 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001034 """
1035
1036 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001037 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001038 for path in bbpaths:
1039 dirpath = os.path.join(path, directory)
1040 if os.path.exists(dirpath):
1041 for root, dirs, files in os.walk(dirpath):
1042 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001043 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001044 matches.append(f)
1045
1046 if matches:
1047 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1048
Patrick Williams93c203f2021-10-06 16:15:23 -05001049 def testCookerCommandEvent(self, filepattern):
1050 # Dummy command used by OEQA selftest to test tinfoil without IO
1051 matches = ["A", "B"]
1052 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1053
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001054 def findProviders(self, mc=''):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001055 return bb.providers.findProviders(self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001056
1057 def findBestProvider(self, pn, mc=''):
1058 if pn in self.recipecaches[mc].providers:
1059 filenames = self.recipecaches[mc].providers[pn]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001060 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.databuilder.mcdata[mc], self.recipecaches[mc])
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001061 if eligible is not None:
1062 filename = eligible[0]
1063 else:
1064 filename = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001065 return None, None, None, filename
1066 elif pn in self.recipecaches[mc].pkg_pn:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001067 (latest, latest_f, preferred_ver, preferred_file, required) = bb.providers.findBestProvider(pn, self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
1068 if required and preferred_file is None:
1069 return None, None, None, None
1070 return (latest, latest_f, preferred_ver, preferred_file)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001071 else:
1072 return None, None, None, None
1073
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001074 def findConfigFiles(self, varname):
1075 """
1076 Find config files which are appropriate values for varname.
1077 i.e. MACHINE, DISTRO
1078 """
1079 possible = []
1080 var = varname.lower()
1081
1082 data = self.data
1083 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001084 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001085 for path in bbpaths:
1086 confpath = os.path.join(path, "conf", var)
1087 if os.path.exists(confpath):
1088 for root, dirs, files in os.walk(confpath):
1089 # get all child files, these are appropriate values
1090 for f in files:
1091 val, sep, end = f.rpartition('.')
1092 if end == 'conf':
1093 possible.append(val)
1094
1095 if possible:
1096 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1097
1098 def findInheritsClass(self, klass):
1099 """
1100 Find all recipes which inherit the specified class
1101 """
1102 pkg_list = []
1103
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001104 for pfn in self.recipecaches[''].pkg_fn:
1105 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001106 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001107 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001108
1109 return pkg_list
1110
1111 def generateTargetsTree(self, klass=None, pkgs=None):
1112 """
1113 Generate a dependency tree of buildable targets
1114 Generate an event with the result
1115 """
1116 # if the caller hasn't specified a pkgs list default to universe
1117 if not pkgs:
1118 pkgs = ['universe']
1119 # if inherited_class passed ensure all recipes which inherit the
1120 # specified class are included in pkgs
1121 if klass:
1122 extra_pkgs = self.findInheritsClass(klass)
1123 pkgs = pkgs + extra_pkgs
1124
1125 # generate a dependency tree for all our packages
1126 tree = self.generatePkgDepTreeData(pkgs, 'build')
1127 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1128
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001129 def interactiveMode( self ):
1130 """Drop off into a shell"""
1131 try:
1132 from bb import shell
1133 except ImportError:
1134 parselog.exception("Interactive mode not available")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001135 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001136 else:
1137 shell.start( self )
1138
1139
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001140 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001141 """Handle collections"""
1142 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001143 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001144 if collections:
1145 collection_priorities = {}
1146 collection_depends = {}
1147 collection_list = collections.split()
1148 min_prio = 0
1149 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001150 bb.debug(1,'Processing %s in collection list' % (c))
1151
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001152 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001153 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001154 if priority:
1155 try:
1156 prio = int(priority)
1157 except ValueError:
1158 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1159 errors = True
1160 if min_prio == 0 or prio < min_prio:
1161 min_prio = prio
1162 collection_priorities[c] = prio
1163 else:
1164 collection_priorities[c] = None
1165
1166 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001167 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001168 if deps:
1169 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001170 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001171 except bb.utils.VersionStringException as vse:
1172 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001173 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001174 if dep in collection_list:
1175 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001176 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001177 (op, depver) = opstr.split()
1178 if layerver:
1179 try:
1180 res = bb.utils.vercmp_string_op(layerver, depver, op)
1181 except bb.utils.VersionStringException as vse:
1182 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1183 if not res:
1184 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)
1185 errors = True
1186 else:
1187 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)
1188 errors = True
1189 else:
1190 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1191 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001192 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001193 else:
1194 collection_depends[c] = []
1195
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001196 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001197 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001198 if recs:
1199 try:
1200 recDict = bb.utils.explode_dep_versions2(recs)
1201 except bb.utils.VersionStringException as vse:
1202 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1203 for rec, oplist in list(recDict.items()):
1204 if rec in collection_list:
1205 if oplist:
1206 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001207 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001208 if layerver:
1209 (op, recver) = opstr.split()
1210 try:
1211 res = bb.utils.vercmp_string_op(layerver, recver, op)
1212 except bb.utils.VersionStringException as vse:
1213 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1214 if not res:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001215 parselog.debug3("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)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001216 continue
1217 else:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001218 parselog.debug3("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)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001219 continue
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001220 parselog.debug3("Layer '%s' recommends layer '%s', so we are adding it", c, rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001221 collection_depends[c].append(rec)
1222 else:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001223 parselog.debug3("Layer '%s' recommends layer '%s', but this layer is not enabled in your configuration", c, rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001224
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001225 # Recursively work out collection priorities based on dependencies
1226 def calc_layer_priority(collection):
1227 if not collection_priorities[collection]:
1228 max_depprio = min_prio
1229 for dep in collection_depends[collection]:
1230 calc_layer_priority(dep)
1231 depprio = collection_priorities[dep]
1232 if depprio > max_depprio:
1233 max_depprio = depprio
1234 max_depprio += 1
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001235 parselog.debug("Calculated priority of layer %s as %d", collection, max_depprio)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001236 collection_priorities[collection] = max_depprio
1237
1238 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1239 for c in collection_list:
1240 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001241 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001242 if regex is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001243 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1244 errors = True
1245 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001246 elif regex == "":
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001247 parselog.debug("BBFILE_PATTERN_%s is empty" % c)
Brad Bishop19323692019-04-05 15:28:33 -04001248 cre = re.compile('^NULL$')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001249 errors = False
1250 else:
1251 try:
1252 cre = re.compile(regex)
1253 except re.error:
1254 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1255 errors = True
1256 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001257 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001258 if errors:
1259 # We've already printed the actual error(s)
1260 raise CollectionError("Errors during parsing layer configuration")
1261
1262 def buildSetVars(self):
1263 """
1264 Setup any variables needed before starting a build
1265 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001266 t = time.gmtime()
1267 for mc in self.databuilder.mcdata:
1268 ds = self.databuilder.mcdata[mc]
1269 if not ds.getVar("BUILDNAME", False):
1270 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1271 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1272 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1273 ds.setVar("TIME", time.strftime('%H%M%S', t))
1274
1275 def reset_mtime_caches(self):
1276 """
1277 Reset mtime caches - this is particularly important when memory resident as something
1278 which is cached is not unlikely to have changed since the last invocation (e.g. a
1279 file associated with a recipe might have been modified by the user).
1280 """
1281 build.reset_cache()
1282 bb.fetch._checksum_cache.mtime_cache.clear()
1283 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1284 if siggen_cache:
1285 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001286
Andrew Geissler5a43b432020-06-13 10:46:56 -05001287 def matchFiles(self, bf, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001288 """
1289 Find the .bb files which match the expression in 'buildfile'.
1290 """
1291 if bf.startswith("/") or bf.startswith("../"):
1292 bf = os.path.abspath(bf)
1293
Patrick Williamse760df82023-05-26 11:10:49 -05001294 collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)}
1295 filelist, masked, searchdirs = collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001296 try:
1297 os.stat(bf)
1298 bf = os.path.abspath(bf)
1299 return [bf]
1300 except OSError:
1301 regexp = re.compile(bf)
1302 matches = []
1303 for f in filelist:
1304 if regexp.search(f) and os.path.isfile(f):
1305 matches.append(f)
1306 return matches
1307
Andrew Geissler5a43b432020-06-13 10:46:56 -05001308 def matchFile(self, buildfile, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001309 """
1310 Find the .bb file which matches the expression in 'buildfile'.
1311 Raise an error if multiple files
1312 """
Andrew Geissler5a43b432020-06-13 10:46:56 -05001313 matches = self.matchFiles(buildfile, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001314 if len(matches) != 1:
1315 if matches:
1316 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1317 if matches:
1318 for f in matches:
1319 msg += "\n %s" % f
1320 parselog.error(msg)
1321 else:
1322 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1323 raise NoSpecificMatch
1324 return matches[0]
1325
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001326 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001327 """
1328 Build the file matching regexp buildfile
1329 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001330 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001331
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001332 # Too many people use -b because they think it's how you normally
1333 # specify a target to be built, so show a warning
1334 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1335
1336 self.buildFileInternal(buildfile, task)
1337
1338 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1339 """
1340 Build the file matching regexp buildfile
1341 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001342
1343 # Parse the configuration here. We need to do it explicitly here since
1344 # buildFile() doesn't use the cache
1345 self.parseConfiguration()
1346
1347 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001348 if task is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001349 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001350 if not task.startswith("do_"):
1351 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001352
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001353 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001354 fn = self.matchFile(fn, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001355
1356 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001357 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001358
Andrew Geissler5a43b432020-06-13 10:46:56 -05001359 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001360
Patrick Williamse760df82023-05-26 11:10:49 -05001361 layername = self.collections[mc].calc_bbfile_priority(fn)[2]
1362 infos = bb_caches[mc].parse(fn, self.collections[mc].get_file_appends(fn), layername)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001363 infos = dict(infos)
1364
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001365 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001366 try:
1367 info_array = infos[fn]
1368 except KeyError:
1369 bb.fatal("%s does not exist" % fn)
1370
1371 if info_array[0].skipped:
1372 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1373
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001374 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001375
1376 # Tweak some variables
1377 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001378 self.recipecaches[mc].ignored_dependencies = set()
1379 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001380 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001381
1382 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001383 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1384 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001385 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1386 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001387
Andrew Geissler517393d2023-01-13 08:55:19 -06001388 bb.parse.siggen.setup_datacache(self.recipecaches)
1389
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001390 # Invalidate task for target if force mode active
1391 if self.configuration.force:
1392 logger.verbose("Invalidate task %s, %s", task, fn)
Andrew Geissler517393d2023-01-13 08:55:19 -06001393 bb.parse.siggen.invalidate_task(task, fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001394
1395 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001396 taskdata = {}
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001397 taskdata[mc] = bb.taskdata.TaskData(self.configuration.halt)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001398 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001399
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001400 if quietlog:
1401 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1402 bb.runqueue.logger.setLevel(logging.WARNING)
1403
1404 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1405 if fireevents:
1406 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Andrew Geissler517393d2023-01-13 08:55:19 -06001407 bb.event.enable_heartbeat()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001408
1409 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001410 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001411
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001412 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001413
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001414 def buildFileIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001415
1416 msg = None
1417 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001418 if halt or self.state == state.forceshutdown:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001419 rq.finish_runqueue(True)
1420 msg = "Forced shutdown"
1421 interrupted = 2
1422 elif self.state == state.shutdown:
1423 rq.finish_runqueue(False)
1424 msg = "Stopped build"
1425 interrupted = 1
1426 failures = 0
1427 try:
1428 retval = rq.execute_runqueue()
1429 except runqueue.TaskFailure as exc:
1430 failures += len(exc.args)
1431 retval = False
1432 except SystemExit as exc:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001433 if quietlog:
1434 bb.runqueue.logger.setLevel(rqloglevel)
Andrew Geissler517393d2023-01-13 08:55:19 -06001435 return bb.server.process.idleFinish(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001436
1437 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001438 if fireevents:
1439 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, item, failures, interrupted), self.databuilder.mcdata[mc])
Andrew Geissler517393d2023-01-13 08:55:19 -06001440 bb.event.disable_heartbeat()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001441 # We trashed self.recipecaches above
Andrew Geissler028142b2023-05-05 11:29:21 -05001442 self._parsecache_set(False)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001443 self.configuration.limited_deps = False
1444 bb.parse.siggen.reset(self.data)
1445 if quietlog:
1446 bb.runqueue.logger.setLevel(rqloglevel)
Andrew Geissler517393d2023-01-13 08:55:19 -06001447 return bb.server.process.idleFinish(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001448 if retval is True:
1449 return True
1450 return retval
1451
Andrew Geissler635e0e42020-08-21 15:58:33 -05001452 self.idleCallBackRegister(buildFileIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001453
Andrew Geissler220dafd2023-10-04 10:18:08 -05001454 def getTaskSignatures(self, target, tasks):
1455 sig = []
1456 getAllTaskSignatures = False
1457
1458 if not tasks:
1459 tasks = ["do_build"]
1460 getAllTaskSignatures = True
1461
1462 for task in tasks:
1463 taskdata, runlist = self.buildTaskData(target, task, self.configuration.halt)
1464 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
1465 rq.rqdata.prepare()
1466
1467 for l in runlist:
1468 mc, pn, taskname, fn = l
1469
1470 taskdep = rq.rqdata.dataCaches[mc].task_deps[fn]
1471 for t in taskdep['tasks']:
1472 if t in taskdep['nostamp'] or "setscene" in t:
1473 continue
1474 tid = bb.runqueue.build_tid(mc, fn, t)
1475
1476 if t in task or getAllTaskSignatures:
1477 try:
1478 rq.rqdata.prepare_task_hash(tid)
1479 sig.append([pn, t, rq.rqdata.get_task_unihash(tid)])
1480 except KeyError:
1481 sig.append(self.getTaskSignatures(target, [t])[0])
1482
1483 return sig
1484
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001485 def buildTargets(self, targets, task):
1486 """
1487 Attempt to build the targets specified
1488 """
1489
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001490 def buildTargetsIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001491 msg = None
1492 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001493 if halt or self.state == state.forceshutdown:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001494 bb.event._should_exit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001495 rq.finish_runqueue(True)
1496 msg = "Forced shutdown"
1497 interrupted = 2
1498 elif self.state == state.shutdown:
1499 rq.finish_runqueue(False)
1500 msg = "Stopped build"
1501 interrupted = 1
1502 failures = 0
1503 try:
1504 retval = rq.execute_runqueue()
1505 except runqueue.TaskFailure as exc:
1506 failures += len(exc.args)
1507 retval = False
1508 except SystemExit as exc:
Andrew Geissler517393d2023-01-13 08:55:19 -06001509 return bb.server.process.idleFinish(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001510
1511 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001512 try:
1513 for mc in self.multiconfigs:
1514 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1515 finally:
Andrew Geissler517393d2023-01-13 08:55:19 -06001516 bb.event.disable_heartbeat()
1517 return bb.server.process.idleFinish(msg)
1518
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001519 if retval is True:
1520 return True
1521 return retval
1522
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001523 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001524 self.buildSetVars()
1525
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001526 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001527 if task is None:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001528 task = self.configuration.cmd
1529
1530 if not task.startswith("do_"):
1531 task = "do_%s" % task
1532
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001533 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1534
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001535 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001536
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001537 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.halt)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001538
1539 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001540
1541 # make targets to always look as <target>:do_<task>
1542 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001543 for target in runlist:
1544 if target[0]:
Brad Bishop15ae2502019-06-18 21:44:24 -04001545 ntargets.append("mc:%s:%s:%s" % (target[0], target[1], target[2]))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001546 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001547
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001548 for mc in self.multiconfigs:
1549 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Andrew Geissler517393d2023-01-13 08:55:19 -06001550 bb.event.enable_heartbeat()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001551
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001552 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001553 if 'universe' in targets:
1554 rq.rqdata.warn_multi_bb = True
1555
Andrew Geissler635e0e42020-08-21 15:58:33 -05001556 self.idleCallBackRegister(buildTargetsIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001557
1558
1559 def getAllKeysWithFlags(self, flaglist):
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001560 def dummy_autorev(d):
1561 return
1562
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001563 dump = {}
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001564 # Horrible but for now we need to avoid any sideeffects of autorev being called
1565 saved = bb.fetch2.get_autorev
1566 bb.fetch2.get_autorev = dummy_autorev
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001567 for k in self.data.keys():
1568 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001569 expand = True
1570 flags = self.data.getVarFlags(k)
1571 if flags and "func" in flags and "python" in flags:
1572 expand = False
1573 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001574 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1575 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001576 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001577 'history' : self.data.varhistory.variable(k),
1578 }
1579 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001580 if flags and d in flags:
1581 dump[k][d] = flags[d]
1582 else:
1583 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001584 except Exception as e:
1585 print(e)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001586 bb.fetch2.get_autorev = saved
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001587 return dump
1588
1589
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001590 def updateCacheSync(self):
1591 if self.state == state.running:
1592 return
1593
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001594 if not self.baseconfig_valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001595 logger.debug("Reloading base configuration data")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001596 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001597 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001598
1599 # This is called for all async commands when self.state != running
1600 def updateCache(self):
1601 if self.state == state.running:
1602 return
1603
1604 if self.state in (state.shutdown, state.forceshutdown, state.error):
1605 if hasattr(self.parser, 'shutdown'):
Andrew Geissler9aee5002022-03-30 16:27:02 +00001606 self.parser.shutdown(clean=False)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001607 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001608 raise bb.BBHandledException()
1609
1610 if self.state != state.parsing:
1611 self.updateCacheSync()
1612
1613 if self.state != state.parsing and not self.parsecache_valid:
Andrew Geissler8f840682023-07-21 09:09:43 -05001614 bb.server.process.serverlog("Parsing started")
Andrew Geissler220dafd2023-10-04 10:18:08 -05001615 self.parsewatched = {}
Patrick Williamsde0582f2022-04-08 10:23:27 -05001616
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001617 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001618 self.parseConfiguration ()
1619 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001620 for mc in self.multiconfigs:
1621 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001622
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001623 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001624 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001625 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001626
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001627 for dep in self.configuration.extra_assume_provided:
1628 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001629
Andrew Geissler5a43b432020-06-13 10:46:56 -05001630 mcfilelist = {}
1631 total_masked = 0
1632 searchdirs = set()
1633 for mc in self.multiconfigs:
Andrew Geissler5a43b432020-06-13 10:46:56 -05001634 (filelist, masked, search) = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
1635
1636 mcfilelist[mc] = filelist
1637 total_masked += masked
1638 searchdirs |= set(search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001639
Andrew Geissler220dafd2023-10-04 10:18:08 -05001640 # Add mtimes for directories searched for bb/bbappend files
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001641 for dirent in searchdirs:
Andrew Geissler220dafd2023-10-04 10:18:08 -05001642 self.add_filewatch([(dirent, bb.parse.cached_mtime_noerror(dirent))])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001643
Andrew Geissler5a43b432020-06-13 10:46:56 -05001644 self.parser = CookerParser(self, mcfilelist, total_masked)
Andrew Geissler028142b2023-05-05 11:29:21 -05001645 self._parsecache_set(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001646
1647 self.state = state.parsing
1648
1649 if not self.parser.parse_next():
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001650 collectlog.debug("parsing complete")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001651 if self.parser.error:
1652 raise bb.BBHandledException()
1653 self.show_appends_with_no_recipes()
1654 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001655 for mc in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001656 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 -05001657 self.state = state.running
1658
1659 # Send an event listing all stamps reachable after parsing
1660 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001661 for mc in self.multiconfigs:
1662 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1663 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001664 return None
1665
1666 return True
1667
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001668 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001669
1670 # Return a copy, don't modify the original
1671 pkgs_to_build = pkgs_to_build[:]
1672
Andrew Geissler595f6302022-01-24 19:11:47 +00001673 if not pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001674 raise NothingToBuild
1675
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001676 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001677 for pkg in pkgs_to_build.copy():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001678 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001679 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Brad Bishop15ae2502019-06-18 21:44:24 -04001680 if pkg.startswith("multiconfig:"):
1681 pkgs_to_build.remove(pkg)
1682 pkgs_to_build.append(pkg.replace("multiconfig:", "mc:"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001683
1684 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001685 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001686 for mc in self.multiconfigs:
1687 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1688 for t in self.recipecaches[mc].world_target:
1689 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001690 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001691 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001692
1693 if 'universe' in pkgs_to_build:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001694 parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001695 parselog.debug("collating packages for \"universe\"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001696 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001697 for mc in self.multiconfigs:
1698 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001699 if task:
1700 foundtask = False
1701 for provider_fn in self.recipecaches[mc].providers[t]:
1702 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1703 foundtask = True
1704 break
1705 if not foundtask:
1706 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1707 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001708 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001709 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001710 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001711
1712 return pkgs_to_build
1713
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001714 def pre_serve(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001715 return
1716
1717 def post_serve(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001718 self.shutdown(force=True)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001719 prserv.serv.auto_shutdown()
Patrick Williams45852732022-04-02 08:58:32 -05001720 if hasattr(bb.parse, "siggen"):
1721 bb.parse.siggen.exit()
Brad Bishop08902b02019-08-20 09:16:51 -04001722 if self.hashserv:
1723 self.hashserv.process.terminate()
1724 self.hashserv.process.join()
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001725 if hasattr(self, "data"):
1726 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001727
Andrew Geissler517393d2023-01-13 08:55:19 -06001728 def shutdown(self, force=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001729 if force:
1730 self.state = state.forceshutdown
Patrick Williams8e7b46e2023-05-01 14:19:06 -05001731 bb.event._should_exit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001732 else:
1733 self.state = state.shutdown
1734
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001735 if self.parser:
Andrew Geissler517393d2023-01-13 08:55:19 -06001736 self.parser.shutdown(clean=False)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001737 self.parser.final_cleanup()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001738
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001739 def finishcommand(self):
Andrew Geissler517393d2023-01-13 08:55:19 -06001740 if hasattr(self.parser, 'shutdown'):
1741 self.parser.shutdown(clean=False)
1742 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001743 self.state = state.initial
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001744 bb.event._should_exit.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001745
1746 def reset(self):
Patrick Williams45852732022-04-02 08:58:32 -05001747 if hasattr(bb.parse, "siggen"):
1748 bb.parse.siggen.exit()
Andrew Geissler517393d2023-01-13 08:55:19 -06001749 self.finishcommand()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001750 self.initConfigurationData()
Brad Bishop08902b02019-08-20 09:16:51 -04001751 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001752
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001753 def clientComplete(self):
1754 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001755 self.finishcommand()
1756 self.extraconfigdata = {}
1757 self.command.reset()
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001758 if hasattr(self, "data"):
1759 self.databuilder.reset()
1760 self.data = self.databuilder.data
Andrew Geissler517393d2023-01-13 08:55:19 -06001761 # In theory tinfoil could have modified the base data before parsing,
1762 # ideally need to track if anything did modify the datastore
Andrew Geissler028142b2023-05-05 11:29:21 -05001763 self._parsecache_set(False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001764
1765class CookerExit(bb.event.Event):
1766 """
1767 Notify clients of the Cooker shutdown
1768 """
1769
1770 def __init__(self):
1771 bb.event.Event.__init__(self)
1772
1773
1774class CookerCollectFiles(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001775 def __init__(self, priorities, mc=''):
1776 self.mc = mc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001777 self.bbappends = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001778 # Priorities is a list of tuples, with the second element as the pattern.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001779 # We need to sort the list with the longest pattern first, and so on to
1780 # the shortest. This allows nested layers to be properly evaluated.
1781 self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001782
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001783 def calc_bbfile_priority(self, filename):
Patrick Williamse760df82023-05-26 11:10:49 -05001784 for layername, _, regex, pri in self.bbfile_config_priorities:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001785 if regex.match(filename):
Patrick Williamse760df82023-05-26 11:10:49 -05001786 return pri, regex, layername
1787 return 0, None, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001788
1789 def get_bbfiles(self):
1790 """Get list of default .bb files by reading out the current directory"""
1791 path = os.getcwd()
1792 contents = os.listdir(path)
1793 bbfiles = []
1794 for f in contents:
1795 if f.endswith(".bb"):
1796 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1797 return bbfiles
1798
1799 def find_bbfiles(self, path):
1800 """Find all the .bb and .bbappend files in a directory"""
1801 found = []
1802 for dir, dirs, files in os.walk(path):
1803 for ignored in ('SCCS', 'CVS', '.svn'):
1804 if ignored in dirs:
1805 dirs.remove(ignored)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001806 found += [os.path.join(dir, f) for f in files if (f.endswith(('.bb', '.bbappend')))]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001807
1808 return found
1809
1810 def collect_bbfiles(self, config, eventdata):
1811 """Collect all available .bb build files"""
1812 masked = 0
1813
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001814 collectlog.debug("collecting .bb files")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001815
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001816 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001817
1818 # Sort files by priority
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001819 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem)[0] )
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001820 config.setVar("BBFILES_PRIORITIZED", " ".join(files))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001821
Andrew Geissler595f6302022-01-24 19:11:47 +00001822 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001823 files = self.get_bbfiles()
1824
Andrew Geissler595f6302022-01-24 19:11:47 +00001825 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001826 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1827 bb.event.fire(CookerExit(), eventdata)
1828
Andrew Geissler220dafd2023-10-04 10:18:08 -05001829 # We need to track where we look so that we can know when the cache is invalid. There
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001830 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001831 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001832 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001833 if hasattr(os, 'scandir'):
1834 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001835 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001836
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001837 def ourlistdir(d):
1838 searchdirs.append(d)
1839 return origlistdir(d)
1840
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001841 def ourscandir(d):
1842 searchdirs.append(d)
1843 return origscandir(d)
1844
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001845 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001846 if hasattr(os, 'scandir'):
1847 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001848 try:
1849 # Can't use set here as order is important
1850 newfiles = []
1851 for f in files:
1852 if os.path.isdir(f):
1853 dirfiles = self.find_bbfiles(f)
1854 for g in dirfiles:
1855 if g not in newfiles:
1856 newfiles.append(g)
1857 else:
1858 globbed = glob.glob(f)
1859 if not globbed and os.path.exists(f):
1860 globbed = [f]
1861 # glob gives files in order on disk. Sort to be deterministic.
1862 for g in sorted(globbed):
1863 if g not in newfiles:
1864 newfiles.append(g)
1865 finally:
1866 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001867 if hasattr(os, 'scandir'):
1868 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001869
1870 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001871
1872 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001873 # First validate the individual regular expressions and ignore any
1874 # that do not compile
1875 bbmasks = []
1876 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001877 # When constructing an older style single regex, it's possible for BBMASK
1878 # to end up beginning with '|', which matches and masks _everything_.
1879 if mask.startswith("|"):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001880 collectlog.warning("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001881 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001882 try:
1883 re.compile(mask)
1884 bbmasks.append(mask)
Andrew Geissler78b72792022-06-14 06:47:25 -05001885 except re.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001886 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1887
1888 # Then validate the combined regular expressions. This should never
1889 # fail, but better safe than sorry...
1890 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001891 try:
1892 bbmask_compiled = re.compile(bbmask)
Andrew Geissler78b72792022-06-14 06:47:25 -05001893 except re.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001894 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1895 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001896
1897 bbfiles = []
1898 bbappend = []
1899 for f in newfiles:
1900 if bbmask and bbmask_compiled.search(f):
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001901 collectlog.debug("skipping masked file %s", f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001902 masked += 1
1903 continue
1904 if f.endswith('.bb'):
1905 bbfiles.append(f)
1906 elif f.endswith('.bbappend'):
1907 bbappend.append(f)
1908 else:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001909 collectlog.debug("skipping %s: unknown file extension", f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001910
1911 # Build a list of .bbappend files for each .bb file
1912 for f in bbappend:
1913 base = os.path.basename(f).replace('.bbappend', '.bb')
1914 self.bbappends.append((base, f))
1915
1916 # Find overlayed recipes
1917 # bbfiles will be in priority order which makes this easy
1918 bbfile_seen = dict()
1919 self.overlayed = defaultdict(list)
1920 for f in reversed(bbfiles):
1921 base = os.path.basename(f)
1922 if base not in bbfile_seen:
1923 bbfile_seen[base] = f
1924 else:
1925 topfile = bbfile_seen[base]
1926 self.overlayed[topfile].append(f)
1927
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001928 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001929
1930 def get_file_appends(self, fn):
1931 """
1932 Returns a list of .bbappend files to apply to fn
1933 """
1934 filelist = []
1935 f = os.path.basename(fn)
1936 for b in self.bbappends:
1937 (bbappend, filename) = b
1938 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1939 filelist.append(filename)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001940 return tuple(filelist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001941
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001942 def collection_priorities(self, pkgfns, fns, d):
1943 # Return the priorities of the entries in pkgfns
1944 # Also check that all the regexes in self.bbfile_config_priorities are used
1945 # (but to do that we need to ensure skipped recipes aren't counted, nor
1946 # collections in BBFILE_PATTERN_IGNORE_EMPTY)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001947
1948 priorities = {}
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001949 seen = set()
1950 matched = set()
1951
1952 matched_regex = set()
1953 unmatched_regex = set()
1954 for _, _, regex, _ in self.bbfile_config_priorities:
1955 unmatched_regex.add(regex)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001956
1957 # Calculate priorities for each file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001958 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001959 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Patrick Williamse760df82023-05-26 11:10:49 -05001960 priorities[p], regex, _ = self.calc_bbfile_priority(realfn)
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001961 if regex in unmatched_regex:
1962 matched_regex.add(regex)
1963 unmatched_regex.remove(regex)
1964 seen.add(realfn)
1965 if regex:
1966 matched.add(realfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001967
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001968 if unmatched_regex:
1969 # Account for bbappend files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001970 for b in self.bbappends:
1971 (bbfile, append) = b
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001972 seen.add(append)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001973
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001974 # Account for skipped recipes
1975 seen.update(fns)
1976
1977 seen.difference_update(matched)
1978
1979 def already_matched(fn):
1980 for regex in matched_regex:
1981 if regex.match(fn):
1982 return True
1983 return False
1984
1985 for unmatch in unmatched_regex.copy():
1986 for fn in seen:
1987 if unmatch.match(fn):
1988 # If the bbappend or file was already matched by another regex, skip it
1989 # e.g. for a layer within a layer, the outer regex could match, the inner
1990 # regex may match nothing and we should warn about that
1991 if already_matched(fn):
1992 continue
1993 unmatched_regex.remove(unmatch)
1994 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001995
1996 for collection, pattern, regex, _ in self.bbfile_config_priorities:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001997 if regex in unmatched_regex:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001998 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Andrew Geissler5a43b432020-06-13 10:46:56 -05001999 collectlog.warning("No bb files in %s matched BBFILE_PATTERN_%s '%s'" % (self.mc if self.mc else 'default',
2000 collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002001
2002 return priorities
2003
2004class ParsingFailure(Exception):
2005 def __init__(self, realexception, recipe):
2006 self.realexception = realexception
2007 self.recipe = recipe
2008 Exception.__init__(self, realexception, recipe)
2009
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002010class Parser(multiprocessing.Process):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002011 def __init__(self, jobs, results, quit, profile):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002012 self.jobs = jobs
2013 self.results = results
2014 self.quit = quit
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002015 multiprocessing.Process.__init__(self)
2016 self.context = bb.utils.get_context().copy()
2017 self.handlers = bb.event.get_class_handlers().copy()
2018 self.profile = profile
Andrew Geissler9aee5002022-03-30 16:27:02 +00002019 self.queue_signals = False
2020 self.signal_received = []
2021 self.signal_threadlock = threading.Lock()
2022
2023 def catch_sig(self, signum, frame):
2024 if self.queue_signals:
2025 self.signal_received.append(signum)
2026 else:
2027 self.handle_sig(signum, frame)
2028
2029 def handle_sig(self, signum, frame):
2030 if signum == signal.SIGTERM:
2031 signal.signal(signal.SIGTERM, signal.SIG_DFL)
2032 os.kill(os.getpid(), signal.SIGTERM)
2033 elif signum == signal.SIGINT:
2034 signal.default_int_handler(signum, frame)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002035
2036 def run(self):
2037
2038 if not self.profile:
2039 self.realrun()
2040 return
2041
2042 try:
2043 import cProfile as profile
2044 except:
2045 import profile
2046 prof = profile.Profile()
2047 try:
2048 profile.Profile.runcall(prof, self.realrun)
2049 finally:
2050 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
2051 prof.dump_stats(logfile)
2052
2053 def realrun(self):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002054 # Signal handling here is hard. We must not terminate any process or thread holding the write
2055 # lock for the event stream as it will not be released, ever, and things will hang.
2056 # Python handles signals in the main thread/process but they can be raised from any thread and
2057 # we want to defer processing of any SIGTERM/SIGINT signal until we're outside the critical section
2058 # and don't hold the lock (see server/process.py). We therefore always catch the signals (so any
2059 # new thread should also do so) and we defer handling but we handle with the local thread lock
2060 # held (a threading lock, not a multiprocessing one) so that no other thread in the process
2061 # can be in the critical section.
2062 signal.signal(signal.SIGTERM, self.catch_sig)
2063 signal.signal(signal.SIGHUP, signal.SIG_DFL)
2064 signal.signal(signal.SIGINT, self.catch_sig)
2065 bb.utils.set_process_name(multiprocessing.current_process().name)
2066 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2067 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002068
2069 pending = []
Andrew Geissler517393d2023-01-13 08:55:19 -06002070 havejobs = True
Andrew Geissler9aee5002022-03-30 16:27:02 +00002071 try:
Andrew Geissler517393d2023-01-13 08:55:19 -06002072 while havejobs or pending:
2073 if self.quit.is_set():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002074 break
Andrew Geissler9aee5002022-03-30 16:27:02 +00002075
Andrew Geissler517393d2023-01-13 08:55:19 -06002076 job = None
2077 try:
2078 job = self.jobs.pop()
2079 except IndexError:
2080 havejobs = False
2081 if job:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002082 result = self.parse(*job)
2083 # Clear the siggen cache after parsing to control memory usage, its huge
2084 bb.parse.siggen.postparsing_clean_cache()
Andrew Geissler9aee5002022-03-30 16:27:02 +00002085 pending.append(result)
Andrew Geissler517393d2023-01-13 08:55:19 -06002086
2087 if pending:
2088 try:
2089 result = pending.pop()
2090 self.results.put(result, timeout=0.05)
2091 except queue.Full:
2092 pending.append(result)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002093 finally:
2094 self.results.close()
2095 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002096
Patrick Williamse760df82023-05-26 11:10:49 -05002097 def parse(self, mc, cache, filename, appends, layername):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002098 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002099 origfilter = bb.event.LogHandler.filter
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002100 # Record the filename we're parsing into any events generated
2101 def parse_filter(self, record):
2102 record.taskpid = bb.event.worker_pid
2103 record.fn = filename
2104 return True
2105
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002106 # Reset our environment and handlers to the original settings
2107 bb.utils.set_context(self.context.copy())
2108 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002109 bb.event.LogHandler.filter = parse_filter
2110
Patrick Williamse760df82023-05-26 11:10:49 -05002111 return True, mc, cache.parse(filename, appends, layername)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002112 except Exception as exc:
2113 tb = sys.exc_info()[2]
2114 exc.recipe = filename
2115 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002116 return True, None, exc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002117 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
2118 # and for example a worker thread doesn't just exit on its own in response to
2119 # a SystemExit event for example.
2120 except BaseException as exc:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002121 return True, None, ParsingFailure(exc, filename)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002122 finally:
2123 bb.event.LogHandler.filter = origfilter
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002124
2125class CookerParser(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002126 def __init__(self, cooker, mcfilelist, masked):
2127 self.mcfilelist = mcfilelist
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002128 self.cooker = cooker
2129 self.cfgdata = cooker.data
2130 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002131 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002132
2133 # Accounting statistics
2134 self.parsed = 0
2135 self.cached = 0
2136 self.error = 0
2137 self.masked = masked
2138
2139 self.skipped = 0
2140 self.virtuals = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002141
2142 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002143 self.process_names = []
2144
Andrew Geissler5a43b432020-06-13 10:46:56 -05002145 self.bb_caches = bb.cache.MulticonfigCache(self.cfgbuilder, self.cfghash, cooker.caches_array)
2146 self.fromcache = set()
2147 self.willparse = set()
2148 for mc in self.cooker.multiconfigs:
2149 for filename in self.mcfilelist[mc]:
2150 appends = self.cooker.collections[mc].get_file_appends(filename)
Patrick Williamse760df82023-05-26 11:10:49 -05002151 layername = self.cooker.collections[mc].calc_bbfile_priority(filename)[2]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002152 if not self.bb_caches[mc].cacheValid(filename, appends):
Patrick Williamse760df82023-05-26 11:10:49 -05002153 self.willparse.add((mc, self.bb_caches[mc], filename, appends, layername))
Andrew Geissler5a43b432020-06-13 10:46:56 -05002154 else:
Patrick Williamse760df82023-05-26 11:10:49 -05002155 self.fromcache.add((mc, self.bb_caches[mc], filename, appends, layername))
Andrew Geissler5a43b432020-06-13 10:46:56 -05002156
2157 self.total = len(self.fromcache) + len(self.willparse)
2158 self.toparse = len(self.willparse)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002159 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002160
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002161 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Andrew Geissler5a43b432020-06-13 10:46:56 -05002162 multiprocessing.cpu_count()), self.toparse)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002163
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002164 bb.cache.SiggenRecipeInfo.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002165 self.start()
2166 self.haveshutdown = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002167 self.syncthread = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002168
2169 def start(self):
2170 self.results = self.load_cached()
2171 self.processes = []
2172 if self.toparse:
2173 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002174
Andrew Geissler517393d2023-01-13 08:55:19 -06002175 self.parser_quit = multiprocessing.Event()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002176 self.result_queue = multiprocessing.Queue()
Brad Bishop19323692019-04-05 15:28:33 -04002177
2178 def chunkify(lst,n):
2179 return [lst[i::n] for i in range(n)]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002180 self.jobs = chunkify(list(self.willparse), self.num_processes)
Brad Bishop19323692019-04-05 15:28:33 -04002181
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002182 for i in range(0, self.num_processes):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002183 parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, self.cooker.configuration.profile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002184 parser.start()
2185 self.process_names.append(parser.name)
2186 self.processes.append(parser)
2187
2188 self.results = itertools.chain(self.results, self.parse_generator())
2189
Patrick Williams8e7b46e2023-05-01 14:19:06 -05002190 def shutdown(self, clean=True, eventmsg="Parsing halted due to errors"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002191 if not self.toparse:
2192 return
2193 if self.haveshutdown:
2194 return
2195 self.haveshutdown = True
2196
2197 if clean:
2198 event = bb.event.ParseCompleted(self.cached, self.parsed,
2199 self.skipped, self.masked,
2200 self.virtuals, self.error,
2201 self.total)
2202
2203 bb.event.fire(event, self.cfgdata)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002204 else:
Patrick Williams8e7b46e2023-05-01 14:19:06 -05002205 bb.event.fire(bb.event.ParseError(eventmsg), self.cfgdata)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002206 bb.error("Parsing halted due to errors, see error messages above")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002207
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002208 # Cleanup the queue before call process.join(), otherwise there might be
2209 # deadlocks.
2210 while True:
2211 try:
2212 self.result_queue.get(timeout=0.25)
2213 except queue.Empty:
2214 break
2215
Andrew Geissler517393d2023-01-13 08:55:19 -06002216 def sync_caches():
2217 for c in self.bb_caches.values():
2218 bb.cache.SiggenRecipeInfo.reset()
2219 c.sync()
2220
2221 self.syncthread = threading.Thread(target=sync_caches, name="SyncThread")
2222 self.syncthread.start()
2223
2224 self.parser_quit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002225
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002226 for process in self.processes:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002227 process.join(0.5)
2228
2229 for process in self.processes:
2230 if process.exitcode is None:
2231 os.kill(process.pid, signal.SIGINT)
2232
2233 for process in self.processes:
2234 process.join(0.5)
2235
2236 for process in self.processes:
2237 if process.exitcode is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002238 process.terminate()
Andrew Geissler9aee5002022-03-30 16:27:02 +00002239
2240 for process in self.processes:
2241 process.join()
2242 # Added in 3.7, cleans up zombies
2243 if hasattr(process, "close"):
2244 process.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002245
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002246 bb.codeparser.parser_cache_save()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002247 bb.codeparser.parser_cache_savemerge()
Andrew Geissler517393d2023-01-13 08:55:19 -06002248 bb.cache.SiggenRecipeInfo.reset()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002249 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002250 if self.cooker.configuration.profile:
2251 profiles = []
2252 for i in self.process_names:
2253 logfile = "profile-parse-%s.log" % i
2254 if os.path.exists(logfile):
2255 profiles.append(logfile)
2256
2257 pout = "profile-parse.log.processed"
2258 bb.utils.process_profilelog(profiles, pout = pout)
2259 print("Processed parsing statistics saved to %s" % (pout))
2260
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002261 def final_cleanup(self):
2262 if self.syncthread:
2263 self.syncthread.join()
2264
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002265 def load_cached(self):
Patrick Williamse760df82023-05-26 11:10:49 -05002266 for mc, cache, filename, appends, layername in self.fromcache:
Andrew Geissler517393d2023-01-13 08:55:19 -06002267 infos = cache.loadCached(filename, appends)
2268 yield False, mc, infos
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002269
2270 def parse_generator(self):
Andrew Geissler595f6302022-01-24 19:11:47 +00002271 empty = False
2272 while self.processes or not empty:
2273 for process in self.processes.copy():
2274 if not process.is_alive():
2275 process.join()
2276 self.processes.remove(process)
2277
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002278 if self.parsed >= self.toparse:
2279 break
2280
2281 try:
2282 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002283 except queue.Empty:
Andrew Geissler595f6302022-01-24 19:11:47 +00002284 empty = True
Andrew Geissler9aee5002022-03-30 16:27:02 +00002285 yield None, None, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002286 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002287 empty = False
Andrew Geissler9aee5002022-03-30 16:27:02 +00002288 yield result
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002289
Andrew Geissler595f6302022-01-24 19:11:47 +00002290 if not (self.parsed >= self.toparse):
2291 raise bb.parse.ParseError("Not all recipes parsed, parser thread killed/died? Exiting.", None)
2292
2293
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002294 def parse_next(self):
2295 result = []
2296 parsed = None
2297 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002298 parsed, mc, result = next(self.results)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002299 if isinstance(result, BaseException):
2300 # Turn exceptions back into exceptions
2301 raise result
2302 if parsed is None:
2303 # Timeout, loop back through the main loop
2304 return True
2305
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002306 except StopIteration:
2307 self.shutdown()
2308 return False
2309 except bb.BBHandledException as exc:
2310 self.error += 1
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002311 logger.debug('Failed to parse recipe: %s' % exc.recipe)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002312 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002313 return False
2314 except ParsingFailure as exc:
2315 self.error += 1
2316 logger.error('Unable to parse %s: %s' %
2317 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002318 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002319 return False
2320 except bb.parse.ParseError as exc:
2321 self.error += 1
2322 logger.error(str(exc))
Patrick Williams8e7b46e2023-05-01 14:19:06 -05002323 self.shutdown(clean=False, eventmsg=str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002324 return False
2325 except bb.data_smart.ExpansionError as exc:
2326 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002327 bbdir = os.path.dirname(__file__) + os.sep
2328 etype, value, _ = sys.exc_info()
2329 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2330 logger.error('ExpansionError during parsing %s', value.recipe,
2331 exc_info=(etype, value, tb))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002332 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002333 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002334 except Exception as exc:
2335 self.error += 1
2336 etype, value, tb = sys.exc_info()
2337 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002338 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002339 exc_info=(etype, value, exc.traceback))
2340 else:
2341 # Most likely, an exception occurred during raising an exception
2342 import traceback
2343 logger.error('Exception during parse: %s' % traceback.format_exc())
Andrew Geissler9aee5002022-03-30 16:27:02 +00002344 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002345 return False
2346
2347 self.current += 1
2348 self.virtuals += len(result)
2349 if parsed:
2350 self.parsed += 1
2351 if self.parsed % self.progress_chunk == 0:
2352 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2353 self.cfgdata)
2354 else:
2355 self.cached += 1
2356
2357 for virtualfn, info_array in result:
2358 if info_array[0].skipped:
2359 self.skipped += 1
2360 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Andrew Geissler5a43b432020-06-13 10:46:56 -05002361 self.bb_caches[mc].add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002362 parsed=parsed, watcher = self.cooker.add_filewatch)
2363 return True
2364
2365 def reparse(self, filename):
Andrew Geissler517393d2023-01-13 08:55:19 -06002366 bb.cache.SiggenRecipeInfo.reset()
Andrew Geissler5a43b432020-06-13 10:46:56 -05002367 to_reparse = set()
2368 for mc in self.cooker.multiconfigs:
Patrick Williamse760df82023-05-26 11:10:49 -05002369 layername = self.cooker.collections[mc].calc_bbfile_priority(filename)[2]
2370 to_reparse.add((mc, filename, self.cooker.collections[mc].get_file_appends(filename), layername))
Andrew Geissler5a43b432020-06-13 10:46:56 -05002371
Patrick Williamse760df82023-05-26 11:10:49 -05002372 for mc, filename, appends, layername in to_reparse:
2373 infos = self.bb_caches[mc].parse(filename, appends, layername)
Andrew Geissler5a43b432020-06-13 10:46:56 -05002374 for vfn, info_array in infos:
2375 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)