blob: 599c7ddaa286475807e67e303c7d6cff5e7ddeb9 [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 Williamsf1e5d692016-03-30 15:21:19 -0500306 if consolelog:
307 self.data.setVar("BB_CONSOLELOG", consolelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500308
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500309 self.data.setVar('BB_CMDLINE', self.ui_cmdline)
310
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
312 self.disableDataTracking()
313
Brad Bishop15ae2502019-06-18 21:44:24 -0400314 for mc in self.databuilder.mcdata.values():
Andrew Geissler220dafd2023-10-04 10:18:08 -0500315 self.add_filewatch(mc.getVar("__base_depends", False), configwatcher=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500316
Andrew Geissler028142b2023-05-05 11:29:21 -0500317 self._baseconfig_set(True)
318 self._parsecache_set(False)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500319
320 def handlePRServ(self):
321 # Setup a PR Server based on the new configuration
322 try:
323 self.prhost = prserv.serv.auto_start(self.data)
324 except prserv.serv.PRServiceConfigError as e:
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500325 bb.fatal("Unable to start PR Server, exiting, check the bitbake-cookerdaemon.log")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500326
Brad Bishopa34c0302019-09-23 22:34:48 -0400327 if self.data.getVar("BB_HASHSERVE") == "auto":
328 # Create a new hash server bound to a unix domain socket
Brad Bishop08902b02019-08-20 09:16:51 -0400329 if not self.hashserv:
330 dbfile = (self.data.getVar("PERSISTENT_DIR") or self.data.getVar("CACHE")) + "/hashserv.db"
Andrew Geissler595f6302022-01-24 19:11:47 +0000331 upstream = self.data.getVar("BB_HASHSERVE_UPSTREAM") or None
332 if upstream:
333 import socket
334 try:
335 sock = socket.create_connection(upstream.split(":"), 5)
336 sock.close()
337 except socket.error as e:
Andrew Geissler87f5cff2022-09-30 13:13:31 -0500338 bb.warn("BB_HASHSERVE_UPSTREAM is not valid, unable to connect hash equivalence server at '%s': %s"
Andrew Geissler595f6302022-01-24 19:11:47 +0000339 % (upstream, repr(e)))
340
Brad Bishopa34c0302019-09-23 22:34:48 -0400341 self.hashservaddr = "unix://%s/hashserve.sock" % self.data.getVar("TOPDIR")
Andrew Geissler5199d832021-09-24 16:47:35 -0500342 self.hashserv = hashserv.create_server(
343 self.hashservaddr,
344 dbfile,
345 sync=False,
Andrew Geissler595f6302022-01-24 19:11:47 +0000346 upstream=upstream,
Andrew Geissler5199d832021-09-24 16:47:35 -0500347 )
Patrick Williams213cb262021-08-07 19:21:33 -0500348 self.hashserv.serve_as_process()
Brad Bishop08902b02019-08-20 09:16:51 -0400349 for mc in self.databuilder.mcdata:
Andrew Geissler517393d2023-01-13 08:55:19 -0600350 self.databuilder.mcorigdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishopa34c0302019-09-23 22:34:48 -0400351 self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400352
353 bb.parse.init_parser(self.data)
354
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500355 def enableDataTracking(self):
356 self.configuration.tracking = True
357 if hasattr(self, "data"):
358 self.data.enableTracking()
359
360 def disableDataTracking(self):
361 self.configuration.tracking = False
362 if hasattr(self, "data"):
363 self.data.disableTracking()
364
Andrew Geissler220dafd2023-10-04 10:18:08 -0500365 def revalidateCaches(self):
366 bb.parse.clear_cache()
367
368 clean = True
369 for f in self.configwatched:
370 if not bb.parse.check_mtime(f, self.configwatched[f]):
371 bb.server.process.serverlog("Found %s changed, invalid cache" % f)
372 self._baseconfig_set(False)
373 self._parsecache_set(False)
374 clean = False
375 break
376
377 if clean:
378 for f in self.parsewatched:
379 if not bb.parse.check_mtime(f, self.parsewatched[f]):
380 bb.server.process.serverlog("Found %s changed, invalid cache" % f)
381 self._parsecache_set(False)
382 clean = False
383 break
384
385 if not clean:
386 bb.parse.BBHandler.cached_statements = {}
387
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500388 def parseConfiguration(self):
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600389 self.updateCacheSync()
390
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500391 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500392 nice = self.data.getVar("BB_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500393 if nice:
394 curnice = os.nice(0)
395 nice = int(nice) - curnice
396 buildlog.verbose("Renice to %s " % os.nice(nice))
397
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600398 if self.recipecaches:
399 del self.recipecaches
400 self.multiconfigs = self.databuilder.mcdata.keys()
401 self.recipecaches = {}
402 for mc in self.multiconfigs:
403 self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500404
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500405 self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
Patrick Williamse760df82023-05-26 11:10:49 -0500406 self.collections = {}
407 for mc in self.multiconfigs:
408 self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500409
Andrew Geissler028142b2023-05-05 11:29:21 -0500410 self._parsecache_set(False)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500411
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500412 def updateConfigOpts(self, options, environment, cmdline):
413 self.ui_cmdline = cmdline
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500414 clean = True
415 for o in options:
416 if o in ['prefile', 'postfile']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500417 # Only these options may require a reparse
418 try:
419 if getattr(self.configuration, o) == options[o]:
420 # Value is the same, no need to mark dirty
421 continue
422 except AttributeError:
423 pass
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600424 logger.debug("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500425 print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500426 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500427 if hasattr(self.configuration, o):
428 setattr(self.configuration, o, options[o])
429
430 if self.configuration.writeeventlog:
431 if self.eventlog and self.eventlog[0] != self.configuration.writeeventlog:
432 bb.event.unregister_UIHhandler(self.eventlog[1])
433 if not self.eventlog or self.eventlog[0] != self.configuration.writeeventlog:
434 # we log all events to a file if so directed
435 # register the log file writer as UI Handler
436 writer = EventWriter(self, self.configuration.writeeventlog)
437 EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
438 self.eventlog = (self.configuration.writeeventlog, bb.event.register_UIHhandler(EventLogWriteHandler(writer)))
439
440 bb.msg.loggerDefaultLogLevel = self.configuration.default_loglevel
441 bb.msg.loggerDefaultDomains = self.configuration.debug_domains
442
443 if hasattr(self, "data"):
444 origenv = bb.data.init()
445 for k in environment:
446 origenv.setVar(k, environment[k])
447 self.data.setVar("BB_ORIGENV", origenv)
448
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500449 for k in bb.utils.approved_variables():
450 if k in environment and k not in self.configuration.env:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600451 logger.debug("Updating new environment variable %s to %s" % (k, environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500452 self.configuration.env[k] = environment[k]
453 clean = False
454 if k in self.configuration.env and k not in environment:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600455 logger.debug("Updating environment variable %s (deleted)" % (k))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500456 del self.configuration.env[k]
457 clean = False
458 if k not in self.configuration.env and k not in environment:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500459 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500460 if environment[k] != self.configuration.env[k]:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600461 logger.debug("Updating environment variable %s from %s to %s" % (k, self.configuration.env[k], environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500462 self.configuration.env[k] = environment[k]
463 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500464
465 # Now update all the variables not in the datastore to match
466 self.configuration.env = environment
467
Andrew Geissler220dafd2023-10-04 10:18:08 -0500468 self.revalidateCaches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500469 if not clean:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600470 logger.debug("Base environment change, triggering reparse")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471 self.reset()
472
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500473 def showVersions(self):
474
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500475 (latest_versions, preferred_versions, required) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500476
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500477 logger.plain("%-35s %25s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version", "Required Version")
478 logger.plain("%-35s %25s %25s %25s\n", "===========", "==============", "=================", "================")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500479
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500480 for p in sorted(self.recipecaches[''].pkg_pn):
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500481 preferred = preferred_versions[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500482 latest = latest_versions[p]
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500483 requiredstr = ""
484 preferredstr = ""
485 if required[p]:
486 if preferred[0] is not None:
487 requiredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
488 else:
489 bb.fatal("REQUIRED_VERSION of package %s not available" % p)
490 else:
491 preferredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500492
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500493 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
494
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500495 if preferred == latest:
496 preferredstr = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500497
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500498 logger.plain("%-35s %25s %25s %25s", p, lateststr, preferredstr, requiredstr)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500499
500 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
501 """
502 Show the outer or per-recipe environment
503 """
504 fn = None
505 envdata = None
Brad Bishop15ae2502019-06-18 21:44:24 -0400506 mc = ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500507 if not pkgs_to_build:
508 pkgs_to_build = []
509
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500510 orig_tracking = self.configuration.tracking
511 if not orig_tracking:
512 self.enableDataTracking()
513 self.reset()
Andrew Geissler9aee5002022-03-30 16:27:02 +0000514 # reset() resets to the UI requested value so we have to redo this
515 self.enableDataTracking()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500516
Brad Bishop15ae2502019-06-18 21:44:24 -0400517 def mc_base(p):
518 if p.startswith('mc:'):
519 s = p.split(':')
520 if len(s) == 2:
521 return s[1]
522 return None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500523
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500524 if buildfile:
525 # Parse the configuration here. We need to do it explicitly here since
526 # this showEnvironment() code path doesn't use the cache
527 self.parseConfiguration()
528
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600529 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500530 fn = self.matchFile(fn, mc)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600531 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500532 elif len(pkgs_to_build) == 1:
Brad Bishop15ae2502019-06-18 21:44:24 -0400533 mc = mc_base(pkgs_to_build[0])
534 if not mc:
535 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
536 if pkgs_to_build[0] in set(ignore.split()):
537 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500538
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000539 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.halt, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500540
Brad Bishop15ae2502019-06-18 21:44:24 -0400541 mc = runlist[0][0]
542 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500543
544 if fn:
545 try:
Patrick Williamse760df82023-05-26 11:10:49 -0500546 layername = self.collections[mc].calc_bbfile_priority(fn)[2]
547 envdata = self.databuilder.parseRecipe(fn, self.collections[mc].get_file_appends(fn), layername)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500548 except Exception as e:
549 parselog.exception("Unable to read %s", fn)
550 raise
Brad Bishop15ae2502019-06-18 21:44:24 -0400551 else:
552 if not mc in self.databuilder.mcdata:
Andrew Geissler5082cc72023-09-11 08:41:39 -0400553 bb.fatal('No multiconfig named "%s" found' % mc)
Brad Bishop15ae2502019-06-18 21:44:24 -0400554 envdata = self.databuilder.mcdata[mc]
555 data.expandKeys(envdata)
556 parse.ast.runAnonFuncs(envdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500557
558 # Display history
559 with closing(StringIO()) as env:
560 self.data.inchistory.emit(env)
561 logger.plain(env.getvalue())
562
563 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500564 with closing(StringIO()) as env:
565 data.emit_env(env, envdata, True)
566 logger.plain(env.getvalue())
567
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000568 # emit the metadata which isn't valid shell
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500569 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600570 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500571 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500572
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500573 if not orig_tracking:
574 self.disableDataTracking()
575 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500576
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000577 def buildTaskData(self, pkgs_to_build, task, halt, allowincomplete=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500578 """
579 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
580 """
581 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
582
583 # A task of None means use the default task
584 if task is None:
585 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500586 if not task.startswith("do_"):
587 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500588
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500589 targetlist = self.checkPackages(pkgs_to_build, task)
590 fulltargetlist = []
591 defaulttask_implicit = ''
592 defaulttask_explicit = False
593 wildcard = False
594
595 # Wild card expansion:
Brad Bishop15ae2502019-06-18 21:44:24 -0400596 # Replace string such as "mc:*:bash"
597 # into "mc:A:bash mc:B:bash bash"
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500598 for k in targetlist:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600599 if k.startswith("mc:") and k.count(':') >= 2:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500600 if wildcard:
601 bb.fatal('multiconfig conflict')
602 if k.split(":")[1] == "*":
603 wildcard = True
604 for mc in self.multiconfigs:
605 if mc:
606 fulltargetlist.append(k.replace('*', mc))
607 # implicit default task
608 else:
609 defaulttask_implicit = k.split(":")[2]
610 else:
611 fulltargetlist.append(k)
612 else:
613 defaulttask_explicit = True
614 fulltargetlist.append(k)
615
616 if not defaulttask_explicit and defaulttask_implicit != '':
617 fulltargetlist.append(defaulttask_implicit)
618
619 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600620 taskdata = {}
621 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500622
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600623 for mc in self.multiconfigs:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000624 taskdata[mc] = bb.taskdata.TaskData(halt, skiplist=self.skiplist, allowincomplete=allowincomplete)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600625 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600626 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627
628 current = 0
629 runlist = []
630 for k in fulltargetlist:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500631 origk = k
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600632 mc = ""
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600633 if k.startswith("mc:") and k.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600634 mc = k.split(":")[1]
635 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500636 ktask = task
637 if ":do_" in k:
638 k2 = k.split(":do_")
639 k = k2[0]
640 ktask = k2[1]
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500641
642 if mc not in self.multiconfigs:
643 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named %s" % (origk, mc))
644
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600645 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500646 current += 1
647 if not ktask.startswith("do_"):
648 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600649 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
650 # e.g. in ASSUME_PROVIDED
651 continue
652 fn = taskdata[mc].build_targets[k][0]
653 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600655
Brad Bishop15ae2502019-06-18 21:44:24 -0400656 havemc = False
657 for mc in self.multiconfigs:
658 if taskdata[mc].get_mcdepends():
659 havemc = True
Brad Bishopf058f492019-01-28 23:50:33 -0500660
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800661 # No need to do check providers if there are no mcdeps or not an mc build
Brad Bishop15ae2502019-06-18 21:44:24 -0400662 if havemc or len(self.multiconfigs) > 1:
Andrew Geissler99467da2019-02-25 18:54:23 -0600663 seen = set()
664 new = True
665 # Make sure we can provide the multiconfig dependency
666 while new:
667 mcdeps = set()
668 # Add unresolved first, so we can get multiconfig indirect dependencies on time
669 for mc in self.multiconfigs:
670 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
671 mcdeps |= set(taskdata[mc].get_mcdepends())
672 new = False
Patrick Williams03907ee2022-05-01 06:28:52 -0500673 for k in mcdeps:
674 if k in seen:
675 continue
676 l = k.split(':')
677 depmc = l[2]
678 if depmc not in self.multiconfigs:
679 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named configuration %s" % (k,depmc))
680 else:
681 logger.debug("Adding providers for multiconfig dependency %s" % l[3])
682 taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
683 seen.add(k)
684 new = True
Brad Bishopf058f492019-01-28 23:50:33 -0500685
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600686 for mc in self.multiconfigs:
687 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
688
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600690 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500691
692 def prepareTreeData(self, pkgs_to_build, task):
693 """
694 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
695 """
696
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000697 # We set halt to False here to prevent unbuildable targets raising
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500698 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600699 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500700
701 return runlist, taskdata
702
703 ######## WARNING : this function requires cache_extra to be enabled ########
704
705 def generateTaskDepTreeData(self, pkgs_to_build, task):
706 """
707 Create a dependency graph of pkgs_to_build including reverse dependency
708 information.
709 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500710 if not task.startswith("do_"):
711 task = "do_%s" % task
712
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500713 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600714 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500715 rq.rqdata.prepare()
716 return self.buildDependTree(rq, taskdata)
717
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600718 @staticmethod
719 def add_mc_prefix(mc, pn):
720 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -0400721 return "mc:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600722 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500723
724 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600725 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500726 depend_tree = {}
727 depend_tree["depends"] = {}
728 depend_tree["tdepends"] = {}
729 depend_tree["pn"] = {}
730 depend_tree["rdepends-pn"] = {}
731 depend_tree["packages"] = {}
732 depend_tree["rdepends-pkg"] = {}
733 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500734 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600735 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600737 for mc in taskdata:
738 for name, fn in list(taskdata[mc].get_providermap().items()):
739 pn = self.recipecaches[mc].pkg_fn[fn]
740 pn = self.add_mc_prefix(mc, pn)
741 if name != pn:
742 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
743 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500744
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600745 for tid in rq.rqdata.runtaskentries:
746 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
747 pn = self.recipecaches[mc].pkg_fn[taskfn]
748 pn = self.add_mc_prefix(mc, pn)
749 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500750 if pn not in depend_tree["pn"]:
751 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600752 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500753 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600754 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500755
756 # if we have extra caches, list all attributes they bring in
757 extra_info = []
758 for cache_class in self.caches_array:
759 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
760 cachefields = getattr(cache_class, 'cachefields', [])
761 extra_info = extra_info + cachefields
762
763 # for all attributes stored, add them to the dependency tree
764 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600765 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500766
767
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500768 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
769 if not dotname in depend_tree["tdepends"]:
770 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600771 for dep in rq.rqdata.runtaskentries[tid].depends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800772 (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
773 deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
Andrew Geissler595f6302022-01-24 19:11:47 +0000774 if depmc:
775 depmc = "mc:" + depmc + ":"
776 depend_tree["tdepends"][dotname].append("%s%s.%s" % (depmc, deppn, bb.runqueue.taskname_from_tid(dep)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600777 if taskfn not in seen_fns:
778 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779 packages = []
780
781 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600782 for dep in taskdata[mc].depids[taskfn]:
783 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500784
785 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600786 for rdep in taskdata[mc].rdepids[taskfn]:
787 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500788
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600789 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500790 for package in rdepends:
791 depend_tree["rdepends-pkg"][package] = []
792 for rdepend in rdepends[package]:
793 depend_tree["rdepends-pkg"][package].append(rdepend)
794 packages.append(package)
795
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600796 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500797 for package in rrecs:
798 depend_tree["rrecs-pkg"][package] = []
799 for rdepend in rrecs[package]:
800 depend_tree["rrecs-pkg"][package].append(rdepend)
801 if not package in packages:
802 packages.append(package)
803
804 for package in packages:
805 if package not in depend_tree["packages"]:
806 depend_tree["packages"][package] = {}
807 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600808 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500809 depend_tree["packages"][package]["version"] = version
810
811 return depend_tree
812
813 ######## WARNING : this function requires cache_extra to be enabled ########
814 def generatePkgDepTreeData(self, pkgs_to_build, task):
815 """
816 Create a dependency tree of pkgs_to_build, returning the data.
817 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500818 if not task.startswith("do_"):
819 task = "do_%s" % task
820
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500822
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600823 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500824 depend_tree = {}
825 depend_tree["depends"] = {}
826 depend_tree["pn"] = {}
827 depend_tree["rdepends-pn"] = {}
828 depend_tree["rdepends-pkg"] = {}
829 depend_tree["rrecs-pkg"] = {}
830
831 # if we have extra caches, list all attributes they bring in
832 extra_info = []
833 for cache_class in self.caches_array:
834 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
835 cachefields = getattr(cache_class, 'cachefields', [])
836 extra_info = extra_info + cachefields
837
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600838 tids = []
839 for mc in taskdata:
840 for tid in taskdata[mc].taskentries:
841 tids.append(tid)
842
843 for tid in tids:
844 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
845
846 pn = self.recipecaches[mc].pkg_fn[taskfn]
847 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500848
849 if pn not in depend_tree["pn"]:
850 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600851 depend_tree["pn"][pn]["filename"] = taskfn
852 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500853 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600854 rdepends = self.recipecaches[mc].rundeps[taskfn]
855 rrecs = self.recipecaches[mc].runrecs[taskfn]
856 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500857
858 # for all extra attributes stored, add them to the dependency tree
859 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600860 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500861
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600862 if taskfn not in seen_fns:
863 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500864
865 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500866 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500867 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600868 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
869 fn_provider = taskdata[mc].build_targets[dep][0]
870 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500871 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500872 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600873 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500874 depend_tree["depends"][pn].append(pn_provider)
875
876 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600877 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500878 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600879 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
880 fn_rprovider = taskdata[mc].run_targets[rdep][0]
881 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500882 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600883 pn_rprovider = rdep
884 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500885 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
886
887 depend_tree["rdepends-pkg"].update(rdepends)
888 depend_tree["rrecs-pkg"].update(rrecs)
889
890 return depend_tree
891
892 def generateDepTreeEvent(self, pkgs_to_build, task):
893 """
894 Create a task dependency graph of pkgs_to_build.
895 Generate an event with the result
896 """
897 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
898 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
899
900 def generateDotGraphFiles(self, pkgs_to_build, task):
901 """
902 Create a task dependency graph of pkgs_to_build.
903 Save the result to a set of .dot files.
904 """
905
906 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
907
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500908 with open('pn-buildlist', 'w') as f:
909 for pn in depgraph["pn"]:
910 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500911 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500912
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500913 # Remove old format output files to ensure no confusion with stale data
914 try:
915 os.unlink('pn-depends.dot')
916 except FileNotFoundError:
917 pass
918 try:
919 os.unlink('package-depends.dot')
920 except FileNotFoundError:
921 pass
Brad Bishop79641f22019-09-10 07:20:22 -0400922 try:
923 os.unlink('recipe-depends.dot')
924 except FileNotFoundError:
925 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500926
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500927 with open('task-depends.dot', 'w') as f:
928 f.write("digraph depends {\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400929 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500930 (pn, taskname) = task.rsplit(".", 1)
931 fn = depgraph["pn"][pn]["filename"]
932 version = depgraph["pn"][pn]["version"]
933 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 -0400934 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500935 f.write('"%s" -> "%s"\n' % (task, dep))
936 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500937 logger.info("Task dependencies saved to 'task-depends.dot'")
938
939 def show_appends_with_no_recipes(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500940 appends_without_recipes = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500941 # Determine which bbappends haven't been applied
Andrew Geissler5a43b432020-06-13 10:46:56 -0500942 for mc in self.multiconfigs:
943 # First get list of recipes, including skipped
944 recipefns = list(self.recipecaches[mc].pkg_fn.keys())
945 recipefns.extend(self.skiplist.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500946
Andrew Geissler5a43b432020-06-13 10:46:56 -0500947 # Work out list of bbappends that have been applied
948 applied_appends = []
949 for fn in recipefns:
950 applied_appends.extend(self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500951
Andrew Geissler5a43b432020-06-13 10:46:56 -0500952 appends_without_recipes[mc] = []
953 for _, appendfn in self.collections[mc].bbappends:
954 if not appendfn in applied_appends:
955 appends_without_recipes[mc].append(appendfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500956
Andrew Geissler5a43b432020-06-13 10:46:56 -0500957 msgs = []
958 for mc in sorted(appends_without_recipes.keys()):
959 if appends_without_recipes[mc]:
960 msgs.append('No recipes in %s available for:\n %s' % (mc if mc else 'default',
961 '\n '.join(appends_without_recipes[mc])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500962
Andrew Geissler5a43b432020-06-13 10:46:56 -0500963 if msgs:
964 msg = "\n".join(msgs)
965 warn_only = self.databuilder.mcdata[mc].getVar("BB_DANGLINGAPPENDS_WARNONLY", \
966 False) or "no"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500967 if warn_only.lower() in ("1", "yes", "true"):
968 bb.warn(msg)
969 else:
970 bb.fatal(msg)
971
972 def handlePrefProviders(self):
973
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600974 for mc in self.multiconfigs:
975 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600976 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500977
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600978 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500979 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600980 try:
981 (providee, provider) = p.split(':')
982 except:
983 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
984 continue
985 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
986 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
987 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500988
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500989 def findConfigFilePath(self, configfile):
990 """
991 Find the location on disk of configfile and if it exists and was parsed by BitBake
992 emit the ConfigFilePathFound event with the path to the file.
993 """
994 path = bb.cookerdata.findConfigFile(configfile, self.data)
995 if not path:
996 return
997
998 # Generate a list of parsed configuration files by searching the files
999 # listed in the __depends and __base_depends variables with a .conf suffix.
1000 conffiles = []
1001 dep_files = self.data.getVar('__base_depends', False) or []
1002 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
1003
1004 for f in dep_files:
1005 if f[0].endswith(".conf"):
1006 conffiles.append(f[0])
1007
1008 _, conf, conffile = path.rpartition("conf/")
1009 match = os.path.join(conf, conffile)
1010 # Try and find matches for conf/conffilename.conf as we don't always
1011 # have the full path to the file.
1012 for cfg in conffiles:
1013 if cfg.endswith(match):
1014 bb.event.fire(bb.event.ConfigFilePathFound(path),
1015 self.data)
1016 break
1017
1018 def findFilesMatchingInDir(self, filepattern, directory):
1019 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001020 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001021 'directory' in each BBPATH. i.e. to find all rootfs package classes available
1022 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
1023 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001024 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001025 """
1026
1027 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001028 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001029 for path in bbpaths:
1030 dirpath = os.path.join(path, directory)
1031 if os.path.exists(dirpath):
1032 for root, dirs, files in os.walk(dirpath):
1033 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001034 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001035 matches.append(f)
1036
1037 if matches:
1038 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1039
Patrick Williams93c203f2021-10-06 16:15:23 -05001040 def testCookerCommandEvent(self, filepattern):
1041 # Dummy command used by OEQA selftest to test tinfoil without IO
1042 matches = ["A", "B"]
1043 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1044
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001045 def findProviders(self, mc=''):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001046 return bb.providers.findProviders(self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001047
1048 def findBestProvider(self, pn, mc=''):
1049 if pn in self.recipecaches[mc].providers:
1050 filenames = self.recipecaches[mc].providers[pn]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001051 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.databuilder.mcdata[mc], self.recipecaches[mc])
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001052 if eligible is not None:
1053 filename = eligible[0]
1054 else:
1055 filename = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001056 return None, None, None, filename
1057 elif pn in self.recipecaches[mc].pkg_pn:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001058 (latest, latest_f, preferred_ver, preferred_file, required) = bb.providers.findBestProvider(pn, self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
1059 if required and preferred_file is None:
1060 return None, None, None, None
1061 return (latest, latest_f, preferred_ver, preferred_file)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001062 else:
1063 return None, None, None, None
1064
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001065 def findConfigFiles(self, varname):
1066 """
1067 Find config files which are appropriate values for varname.
1068 i.e. MACHINE, DISTRO
1069 """
1070 possible = []
1071 var = varname.lower()
1072
1073 data = self.data
1074 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001075 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001076 for path in bbpaths:
1077 confpath = os.path.join(path, "conf", var)
1078 if os.path.exists(confpath):
1079 for root, dirs, files in os.walk(confpath):
1080 # get all child files, these are appropriate values
1081 for f in files:
1082 val, sep, end = f.rpartition('.')
1083 if end == 'conf':
1084 possible.append(val)
1085
1086 if possible:
1087 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1088
1089 def findInheritsClass(self, klass):
1090 """
1091 Find all recipes which inherit the specified class
1092 """
1093 pkg_list = []
1094
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001095 for pfn in self.recipecaches[''].pkg_fn:
1096 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001097 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001098 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001099
1100 return pkg_list
1101
1102 def generateTargetsTree(self, klass=None, pkgs=None):
1103 """
1104 Generate a dependency tree of buildable targets
1105 Generate an event with the result
1106 """
1107 # if the caller hasn't specified a pkgs list default to universe
1108 if not pkgs:
1109 pkgs = ['universe']
1110 # if inherited_class passed ensure all recipes which inherit the
1111 # specified class are included in pkgs
1112 if klass:
1113 extra_pkgs = self.findInheritsClass(klass)
1114 pkgs = pkgs + extra_pkgs
1115
1116 # generate a dependency tree for all our packages
1117 tree = self.generatePkgDepTreeData(pkgs, 'build')
1118 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1119
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001120 def interactiveMode( self ):
1121 """Drop off into a shell"""
1122 try:
1123 from bb import shell
1124 except ImportError:
1125 parselog.exception("Interactive mode not available")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001126 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001127 else:
1128 shell.start( self )
1129
1130
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001131 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001132 """Handle collections"""
1133 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001134 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001135 if collections:
1136 collection_priorities = {}
1137 collection_depends = {}
1138 collection_list = collections.split()
1139 min_prio = 0
1140 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001141 bb.debug(1,'Processing %s in collection list' % (c))
1142
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001143 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001144 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001145 if priority:
1146 try:
1147 prio = int(priority)
1148 except ValueError:
1149 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1150 errors = True
1151 if min_prio == 0 or prio < min_prio:
1152 min_prio = prio
1153 collection_priorities[c] = prio
1154 else:
1155 collection_priorities[c] = None
1156
1157 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001158 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001159 if deps:
1160 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001161 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001162 except bb.utils.VersionStringException as vse:
1163 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001164 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001165 if dep in collection_list:
1166 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001167 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001168 (op, depver) = opstr.split()
1169 if layerver:
1170 try:
1171 res = bb.utils.vercmp_string_op(layerver, depver, op)
1172 except bb.utils.VersionStringException as vse:
1173 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1174 if not res:
1175 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)
1176 errors = True
1177 else:
1178 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)
1179 errors = True
1180 else:
1181 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1182 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001183 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001184 else:
1185 collection_depends[c] = []
1186
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001187 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001188 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001189 if recs:
1190 try:
1191 recDict = bb.utils.explode_dep_versions2(recs)
1192 except bb.utils.VersionStringException as vse:
1193 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1194 for rec, oplist in list(recDict.items()):
1195 if rec in collection_list:
1196 if oplist:
1197 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001198 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001199 if layerver:
1200 (op, recver) = opstr.split()
1201 try:
1202 res = bb.utils.vercmp_string_op(layerver, recver, op)
1203 except bb.utils.VersionStringException as vse:
1204 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1205 if not res:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001206 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 -06001207 continue
1208 else:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001209 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 -06001210 continue
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001211 parselog.debug3("Layer '%s' recommends layer '%s', so we are adding it", c, rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001212 collection_depends[c].append(rec)
1213 else:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001214 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 -06001215
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001216 # Recursively work out collection priorities based on dependencies
1217 def calc_layer_priority(collection):
1218 if not collection_priorities[collection]:
1219 max_depprio = min_prio
1220 for dep in collection_depends[collection]:
1221 calc_layer_priority(dep)
1222 depprio = collection_priorities[dep]
1223 if depprio > max_depprio:
1224 max_depprio = depprio
1225 max_depprio += 1
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001226 parselog.debug("Calculated priority of layer %s as %d", collection, max_depprio)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001227 collection_priorities[collection] = max_depprio
1228
1229 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1230 for c in collection_list:
1231 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001232 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001233 if regex is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001234 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1235 errors = True
1236 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001237 elif regex == "":
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001238 parselog.debug("BBFILE_PATTERN_%s is empty" % c)
Brad Bishop19323692019-04-05 15:28:33 -04001239 cre = re.compile('^NULL$')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001240 errors = False
1241 else:
1242 try:
1243 cre = re.compile(regex)
1244 except re.error:
1245 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1246 errors = True
1247 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001248 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001249 if errors:
1250 # We've already printed the actual error(s)
1251 raise CollectionError("Errors during parsing layer configuration")
1252
1253 def buildSetVars(self):
1254 """
1255 Setup any variables needed before starting a build
1256 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001257 t = time.gmtime()
1258 for mc in self.databuilder.mcdata:
1259 ds = self.databuilder.mcdata[mc]
1260 if not ds.getVar("BUILDNAME", False):
1261 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1262 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1263 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1264 ds.setVar("TIME", time.strftime('%H%M%S', t))
1265
1266 def reset_mtime_caches(self):
1267 """
1268 Reset mtime caches - this is particularly important when memory resident as something
1269 which is cached is not unlikely to have changed since the last invocation (e.g. a
1270 file associated with a recipe might have been modified by the user).
1271 """
1272 build.reset_cache()
1273 bb.fetch._checksum_cache.mtime_cache.clear()
1274 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1275 if siggen_cache:
1276 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001277
Andrew Geissler5a43b432020-06-13 10:46:56 -05001278 def matchFiles(self, bf, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001279 """
1280 Find the .bb files which match the expression in 'buildfile'.
1281 """
1282 if bf.startswith("/") or bf.startswith("../"):
1283 bf = os.path.abspath(bf)
1284
Patrick Williamse760df82023-05-26 11:10:49 -05001285 collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)}
1286 filelist, masked, searchdirs = collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001287 try:
1288 os.stat(bf)
1289 bf = os.path.abspath(bf)
1290 return [bf]
1291 except OSError:
1292 regexp = re.compile(bf)
1293 matches = []
1294 for f in filelist:
1295 if regexp.search(f) and os.path.isfile(f):
1296 matches.append(f)
1297 return matches
1298
Andrew Geissler5a43b432020-06-13 10:46:56 -05001299 def matchFile(self, buildfile, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001300 """
1301 Find the .bb file which matches the expression in 'buildfile'.
1302 Raise an error if multiple files
1303 """
Andrew Geissler5a43b432020-06-13 10:46:56 -05001304 matches = self.matchFiles(buildfile, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001305 if len(matches) != 1:
1306 if matches:
1307 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1308 if matches:
1309 for f in matches:
1310 msg += "\n %s" % f
1311 parselog.error(msg)
1312 else:
1313 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1314 raise NoSpecificMatch
1315 return matches[0]
1316
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001317 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001318 """
1319 Build the file matching regexp buildfile
1320 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001321 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001322
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001323 # Too many people use -b because they think it's how you normally
1324 # specify a target to be built, so show a warning
1325 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1326
1327 self.buildFileInternal(buildfile, task)
1328
1329 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1330 """
1331 Build the file matching regexp buildfile
1332 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001333
1334 # Parse the configuration here. We need to do it explicitly here since
1335 # buildFile() doesn't use the cache
1336 self.parseConfiguration()
1337
1338 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001339 if task is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001340 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001341 if not task.startswith("do_"):
1342 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001343
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001344 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001345 fn = self.matchFile(fn, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001346
1347 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001348 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001349
Andrew Geissler5a43b432020-06-13 10:46:56 -05001350 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001351
Patrick Williamse760df82023-05-26 11:10:49 -05001352 layername = self.collections[mc].calc_bbfile_priority(fn)[2]
1353 infos = bb_caches[mc].parse(fn, self.collections[mc].get_file_appends(fn), layername)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001354 infos = dict(infos)
1355
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001356 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001357 try:
1358 info_array = infos[fn]
1359 except KeyError:
1360 bb.fatal("%s does not exist" % fn)
1361
1362 if info_array[0].skipped:
1363 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1364
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001365 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001366
1367 # Tweak some variables
1368 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001369 self.recipecaches[mc].ignored_dependencies = set()
1370 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001371 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001372
1373 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001374 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1375 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001376 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1377 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001378
Andrew Geissler517393d2023-01-13 08:55:19 -06001379 bb.parse.siggen.setup_datacache(self.recipecaches)
1380
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001381 # Invalidate task for target if force mode active
1382 if self.configuration.force:
1383 logger.verbose("Invalidate task %s, %s", task, fn)
Andrew Geissler517393d2023-01-13 08:55:19 -06001384 bb.parse.siggen.invalidate_task(task, fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001385
1386 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001387 taskdata = {}
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001388 taskdata[mc] = bb.taskdata.TaskData(self.configuration.halt)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001389 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001390
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001391 if quietlog:
1392 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1393 bb.runqueue.logger.setLevel(logging.WARNING)
1394
1395 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1396 if fireevents:
1397 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Andrew Geissler517393d2023-01-13 08:55:19 -06001398 bb.event.enable_heartbeat()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001399
1400 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001401 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001402
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001403 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001404
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001405 def buildFileIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001406
1407 msg = None
1408 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001409 if halt or self.state == state.forceshutdown:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001410 rq.finish_runqueue(True)
1411 msg = "Forced shutdown"
1412 interrupted = 2
1413 elif self.state == state.shutdown:
1414 rq.finish_runqueue(False)
1415 msg = "Stopped build"
1416 interrupted = 1
1417 failures = 0
1418 try:
1419 retval = rq.execute_runqueue()
1420 except runqueue.TaskFailure as exc:
1421 failures += len(exc.args)
1422 retval = False
1423 except SystemExit as exc:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001424 if quietlog:
1425 bb.runqueue.logger.setLevel(rqloglevel)
Andrew Geissler517393d2023-01-13 08:55:19 -06001426 return bb.server.process.idleFinish(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001427
1428 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001429 if fireevents:
1430 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 -06001431 bb.event.disable_heartbeat()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001432 # We trashed self.recipecaches above
Andrew Geissler028142b2023-05-05 11:29:21 -05001433 self._parsecache_set(False)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001434 self.configuration.limited_deps = False
1435 bb.parse.siggen.reset(self.data)
1436 if quietlog:
1437 bb.runqueue.logger.setLevel(rqloglevel)
Andrew Geissler517393d2023-01-13 08:55:19 -06001438 return bb.server.process.idleFinish(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001439 if retval is True:
1440 return True
1441 return retval
1442
Andrew Geissler635e0e42020-08-21 15:58:33 -05001443 self.idleCallBackRegister(buildFileIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001444
Andrew Geissler220dafd2023-10-04 10:18:08 -05001445 def getTaskSignatures(self, target, tasks):
1446 sig = []
1447 getAllTaskSignatures = False
1448
1449 if not tasks:
1450 tasks = ["do_build"]
1451 getAllTaskSignatures = True
1452
1453 for task in tasks:
1454 taskdata, runlist = self.buildTaskData(target, task, self.configuration.halt)
1455 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
1456 rq.rqdata.prepare()
1457
1458 for l in runlist:
1459 mc, pn, taskname, fn = l
1460
1461 taskdep = rq.rqdata.dataCaches[mc].task_deps[fn]
1462 for t in taskdep['tasks']:
1463 if t in taskdep['nostamp'] or "setscene" in t:
1464 continue
1465 tid = bb.runqueue.build_tid(mc, fn, t)
1466
1467 if t in task or getAllTaskSignatures:
1468 try:
1469 rq.rqdata.prepare_task_hash(tid)
1470 sig.append([pn, t, rq.rqdata.get_task_unihash(tid)])
1471 except KeyError:
1472 sig.append(self.getTaskSignatures(target, [t])[0])
1473
1474 return sig
1475
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001476 def buildTargets(self, targets, task):
1477 """
1478 Attempt to build the targets specified
1479 """
1480
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001481 def buildTargetsIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001482 msg = None
1483 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001484 if halt or self.state == state.forceshutdown:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001485 bb.event._should_exit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001486 rq.finish_runqueue(True)
1487 msg = "Forced shutdown"
1488 interrupted = 2
1489 elif self.state == state.shutdown:
1490 rq.finish_runqueue(False)
1491 msg = "Stopped build"
1492 interrupted = 1
1493 failures = 0
1494 try:
1495 retval = rq.execute_runqueue()
1496 except runqueue.TaskFailure as exc:
1497 failures += len(exc.args)
1498 retval = False
1499 except SystemExit as exc:
Andrew Geissler517393d2023-01-13 08:55:19 -06001500 return bb.server.process.idleFinish(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001501
1502 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001503 try:
1504 for mc in self.multiconfigs:
1505 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1506 finally:
Andrew Geissler517393d2023-01-13 08:55:19 -06001507 bb.event.disable_heartbeat()
1508 return bb.server.process.idleFinish(msg)
1509
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001510 if retval is True:
1511 return True
1512 return retval
1513
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001514 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001515 self.buildSetVars()
1516
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001517 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001518 if task is None:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001519 task = self.configuration.cmd
1520
1521 if not task.startswith("do_"):
1522 task = "do_%s" % task
1523
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001524 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1525
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001526 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001527
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001528 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.halt)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001529
1530 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001531
1532 # make targets to always look as <target>:do_<task>
1533 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001534 for target in runlist:
1535 if target[0]:
Brad Bishop15ae2502019-06-18 21:44:24 -04001536 ntargets.append("mc:%s:%s:%s" % (target[0], target[1], target[2]))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001537 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001538
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001539 for mc in self.multiconfigs:
1540 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Andrew Geissler517393d2023-01-13 08:55:19 -06001541 bb.event.enable_heartbeat()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001542
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001543 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001544 if 'universe' in targets:
1545 rq.rqdata.warn_multi_bb = True
1546
Andrew Geissler635e0e42020-08-21 15:58:33 -05001547 self.idleCallBackRegister(buildTargetsIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001548
1549
1550 def getAllKeysWithFlags(self, flaglist):
1551 dump = {}
1552 for k in self.data.keys():
1553 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001554 expand = True
1555 flags = self.data.getVarFlags(k)
1556 if flags and "func" in flags and "python" in flags:
1557 expand = False
1558 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001559 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1560 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001561 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001562 'history' : self.data.varhistory.variable(k),
1563 }
1564 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001565 if flags and d in flags:
1566 dump[k][d] = flags[d]
1567 else:
1568 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001569 except Exception as e:
1570 print(e)
1571 return dump
1572
1573
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001574 def updateCacheSync(self):
1575 if self.state == state.running:
1576 return
1577
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001578 if not self.baseconfig_valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001579 logger.debug("Reloading base configuration data")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001580 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001581 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001582
1583 # This is called for all async commands when self.state != running
1584 def updateCache(self):
1585 if self.state == state.running:
1586 return
1587
1588 if self.state in (state.shutdown, state.forceshutdown, state.error):
1589 if hasattr(self.parser, 'shutdown'):
Andrew Geissler9aee5002022-03-30 16:27:02 +00001590 self.parser.shutdown(clean=False)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001591 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001592 raise bb.BBHandledException()
1593
1594 if self.state != state.parsing:
1595 self.updateCacheSync()
1596
1597 if self.state != state.parsing and not self.parsecache_valid:
Andrew Geissler8f840682023-07-21 09:09:43 -05001598 bb.server.process.serverlog("Parsing started")
Andrew Geissler220dafd2023-10-04 10:18:08 -05001599 self.parsewatched = {}
Patrick Williamsde0582f2022-04-08 10:23:27 -05001600
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001601 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001602 self.parseConfiguration ()
1603 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001604 for mc in self.multiconfigs:
1605 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001606
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001607 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001608 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001609 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001610
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001611 for dep in self.configuration.extra_assume_provided:
1612 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001613
Andrew Geissler5a43b432020-06-13 10:46:56 -05001614 mcfilelist = {}
1615 total_masked = 0
1616 searchdirs = set()
1617 for mc in self.multiconfigs:
Andrew Geissler5a43b432020-06-13 10:46:56 -05001618 (filelist, masked, search) = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
1619
1620 mcfilelist[mc] = filelist
1621 total_masked += masked
1622 searchdirs |= set(search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001623
Andrew Geissler220dafd2023-10-04 10:18:08 -05001624 # Add mtimes for directories searched for bb/bbappend files
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001625 for dirent in searchdirs:
Andrew Geissler220dafd2023-10-04 10:18:08 -05001626 self.add_filewatch([(dirent, bb.parse.cached_mtime_noerror(dirent))])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001627
Andrew Geissler5a43b432020-06-13 10:46:56 -05001628 self.parser = CookerParser(self, mcfilelist, total_masked)
Andrew Geissler028142b2023-05-05 11:29:21 -05001629 self._parsecache_set(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001630
1631 self.state = state.parsing
1632
1633 if not self.parser.parse_next():
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001634 collectlog.debug("parsing complete")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001635 if self.parser.error:
1636 raise bb.BBHandledException()
1637 self.show_appends_with_no_recipes()
1638 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001639 for mc in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001640 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 -05001641 self.state = state.running
1642
1643 # Send an event listing all stamps reachable after parsing
1644 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001645 for mc in self.multiconfigs:
1646 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1647 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001648 return None
1649
1650 return True
1651
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001652 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001653
1654 # Return a copy, don't modify the original
1655 pkgs_to_build = pkgs_to_build[:]
1656
Andrew Geissler595f6302022-01-24 19:11:47 +00001657 if not pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001658 raise NothingToBuild
1659
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001660 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001661 for pkg in pkgs_to_build.copy():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001662 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001663 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Brad Bishop15ae2502019-06-18 21:44:24 -04001664 if pkg.startswith("multiconfig:"):
1665 pkgs_to_build.remove(pkg)
1666 pkgs_to_build.append(pkg.replace("multiconfig:", "mc:"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001667
1668 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001669 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001670 for mc in self.multiconfigs:
1671 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1672 for t in self.recipecaches[mc].world_target:
1673 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001674 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001675 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001676
1677 if 'universe' in pkgs_to_build:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001678 parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001679 parselog.debug("collating packages for \"universe\"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001680 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001681 for mc in self.multiconfigs:
1682 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001683 if task:
1684 foundtask = False
1685 for provider_fn in self.recipecaches[mc].providers[t]:
1686 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1687 foundtask = True
1688 break
1689 if not foundtask:
1690 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1691 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001692 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001693 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001694 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001695
1696 return pkgs_to_build
1697
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001698 def pre_serve(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001699 return
1700
1701 def post_serve(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001702 self.shutdown(force=True)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001703 prserv.serv.auto_shutdown()
Patrick Williams45852732022-04-02 08:58:32 -05001704 if hasattr(bb.parse, "siggen"):
1705 bb.parse.siggen.exit()
Brad Bishop08902b02019-08-20 09:16:51 -04001706 if self.hashserv:
1707 self.hashserv.process.terminate()
1708 self.hashserv.process.join()
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001709 if hasattr(self, "data"):
1710 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001711
Andrew Geissler517393d2023-01-13 08:55:19 -06001712 def shutdown(self, force=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001713 if force:
1714 self.state = state.forceshutdown
Patrick Williams8e7b46e2023-05-01 14:19:06 -05001715 bb.event._should_exit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001716 else:
1717 self.state = state.shutdown
1718
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001719 if self.parser:
Andrew Geissler517393d2023-01-13 08:55:19 -06001720 self.parser.shutdown(clean=False)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001721 self.parser.final_cleanup()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001722
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001723 def finishcommand(self):
Andrew Geissler517393d2023-01-13 08:55:19 -06001724 if hasattr(self.parser, 'shutdown'):
1725 self.parser.shutdown(clean=False)
1726 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001727 self.state = state.initial
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001728 bb.event._should_exit.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001729
1730 def reset(self):
Patrick Williams45852732022-04-02 08:58:32 -05001731 if hasattr(bb.parse, "siggen"):
1732 bb.parse.siggen.exit()
Andrew Geissler517393d2023-01-13 08:55:19 -06001733 self.finishcommand()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001734 self.initConfigurationData()
Brad Bishop08902b02019-08-20 09:16:51 -04001735 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001736
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001737 def clientComplete(self):
1738 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001739 self.finishcommand()
1740 self.extraconfigdata = {}
1741 self.command.reset()
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001742 if hasattr(self, "data"):
1743 self.databuilder.reset()
1744 self.data = self.databuilder.data
Andrew Geissler517393d2023-01-13 08:55:19 -06001745 # In theory tinfoil could have modified the base data before parsing,
1746 # ideally need to track if anything did modify the datastore
Andrew Geissler028142b2023-05-05 11:29:21 -05001747 self._parsecache_set(False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001748
1749class CookerExit(bb.event.Event):
1750 """
1751 Notify clients of the Cooker shutdown
1752 """
1753
1754 def __init__(self):
1755 bb.event.Event.__init__(self)
1756
1757
1758class CookerCollectFiles(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001759 def __init__(self, priorities, mc=''):
1760 self.mc = mc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001761 self.bbappends = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001762 # Priorities is a list of tuples, with the second element as the pattern.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001763 # We need to sort the list with the longest pattern first, and so on to
1764 # the shortest. This allows nested layers to be properly evaluated.
1765 self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001766
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001767 def calc_bbfile_priority(self, filename):
Patrick Williamse760df82023-05-26 11:10:49 -05001768 for layername, _, regex, pri in self.bbfile_config_priorities:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001769 if regex.match(filename):
Patrick Williamse760df82023-05-26 11:10:49 -05001770 return pri, regex, layername
1771 return 0, None, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001772
1773 def get_bbfiles(self):
1774 """Get list of default .bb files by reading out the current directory"""
1775 path = os.getcwd()
1776 contents = os.listdir(path)
1777 bbfiles = []
1778 for f in contents:
1779 if f.endswith(".bb"):
1780 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1781 return bbfiles
1782
1783 def find_bbfiles(self, path):
1784 """Find all the .bb and .bbappend files in a directory"""
1785 found = []
1786 for dir, dirs, files in os.walk(path):
1787 for ignored in ('SCCS', 'CVS', '.svn'):
1788 if ignored in dirs:
1789 dirs.remove(ignored)
1790 found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
1791
1792 return found
1793
1794 def collect_bbfiles(self, config, eventdata):
1795 """Collect all available .bb build files"""
1796 masked = 0
1797
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001798 collectlog.debug("collecting .bb files")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001799
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001800 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001801
1802 # Sort files by priority
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001803 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem)[0] )
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001804 config.setVar("BBFILES_PRIORITIZED", " ".join(files))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001805
Andrew Geissler595f6302022-01-24 19:11:47 +00001806 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001807 files = self.get_bbfiles()
1808
Andrew Geissler595f6302022-01-24 19:11:47 +00001809 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001810 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1811 bb.event.fire(CookerExit(), eventdata)
1812
Andrew Geissler220dafd2023-10-04 10:18:08 -05001813 # 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 -05001814 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001815 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001816 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001817 if hasattr(os, 'scandir'):
1818 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001819 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001820
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001821 def ourlistdir(d):
1822 searchdirs.append(d)
1823 return origlistdir(d)
1824
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001825 def ourscandir(d):
1826 searchdirs.append(d)
1827 return origscandir(d)
1828
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001829 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001830 if hasattr(os, 'scandir'):
1831 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001832 try:
1833 # Can't use set here as order is important
1834 newfiles = []
1835 for f in files:
1836 if os.path.isdir(f):
1837 dirfiles = self.find_bbfiles(f)
1838 for g in dirfiles:
1839 if g not in newfiles:
1840 newfiles.append(g)
1841 else:
1842 globbed = glob.glob(f)
1843 if not globbed and os.path.exists(f):
1844 globbed = [f]
1845 # glob gives files in order on disk. Sort to be deterministic.
1846 for g in sorted(globbed):
1847 if g not in newfiles:
1848 newfiles.append(g)
1849 finally:
1850 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001851 if hasattr(os, 'scandir'):
1852 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001853
1854 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001855
1856 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001857 # First validate the individual regular expressions and ignore any
1858 # that do not compile
1859 bbmasks = []
1860 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001861 # When constructing an older style single regex, it's possible for BBMASK
1862 # to end up beginning with '|', which matches and masks _everything_.
1863 if mask.startswith("|"):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001864 collectlog.warning("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001865 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001866 try:
1867 re.compile(mask)
1868 bbmasks.append(mask)
Andrew Geissler78b72792022-06-14 06:47:25 -05001869 except re.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001870 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1871
1872 # Then validate the combined regular expressions. This should never
1873 # fail, but better safe than sorry...
1874 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001875 try:
1876 bbmask_compiled = re.compile(bbmask)
Andrew Geissler78b72792022-06-14 06:47:25 -05001877 except re.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001878 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1879 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001880
1881 bbfiles = []
1882 bbappend = []
1883 for f in newfiles:
1884 if bbmask and bbmask_compiled.search(f):
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001885 collectlog.debug("skipping masked file %s", f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001886 masked += 1
1887 continue
1888 if f.endswith('.bb'):
1889 bbfiles.append(f)
1890 elif f.endswith('.bbappend'):
1891 bbappend.append(f)
1892 else:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001893 collectlog.debug("skipping %s: unknown file extension", f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001894
1895 # Build a list of .bbappend files for each .bb file
1896 for f in bbappend:
1897 base = os.path.basename(f).replace('.bbappend', '.bb')
1898 self.bbappends.append((base, f))
1899
1900 # Find overlayed recipes
1901 # bbfiles will be in priority order which makes this easy
1902 bbfile_seen = dict()
1903 self.overlayed = defaultdict(list)
1904 for f in reversed(bbfiles):
1905 base = os.path.basename(f)
1906 if base not in bbfile_seen:
1907 bbfile_seen[base] = f
1908 else:
1909 topfile = bbfile_seen[base]
1910 self.overlayed[topfile].append(f)
1911
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001912 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001913
1914 def get_file_appends(self, fn):
1915 """
1916 Returns a list of .bbappend files to apply to fn
1917 """
1918 filelist = []
1919 f = os.path.basename(fn)
1920 for b in self.bbappends:
1921 (bbappend, filename) = b
1922 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1923 filelist.append(filename)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001924 return tuple(filelist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001925
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001926 def collection_priorities(self, pkgfns, fns, d):
1927 # Return the priorities of the entries in pkgfns
1928 # Also check that all the regexes in self.bbfile_config_priorities are used
1929 # (but to do that we need to ensure skipped recipes aren't counted, nor
1930 # collections in BBFILE_PATTERN_IGNORE_EMPTY)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001931
1932 priorities = {}
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001933 seen = set()
1934 matched = set()
1935
1936 matched_regex = set()
1937 unmatched_regex = set()
1938 for _, _, regex, _ in self.bbfile_config_priorities:
1939 unmatched_regex.add(regex)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001940
1941 # Calculate priorities for each file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001942 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001943 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Patrick Williamse760df82023-05-26 11:10:49 -05001944 priorities[p], regex, _ = self.calc_bbfile_priority(realfn)
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001945 if regex in unmatched_regex:
1946 matched_regex.add(regex)
1947 unmatched_regex.remove(regex)
1948 seen.add(realfn)
1949 if regex:
1950 matched.add(realfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001951
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001952 if unmatched_regex:
1953 # Account for bbappend files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001954 for b in self.bbappends:
1955 (bbfile, append) = b
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001956 seen.add(append)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001957
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001958 # Account for skipped recipes
1959 seen.update(fns)
1960
1961 seen.difference_update(matched)
1962
1963 def already_matched(fn):
1964 for regex in matched_regex:
1965 if regex.match(fn):
1966 return True
1967 return False
1968
1969 for unmatch in unmatched_regex.copy():
1970 for fn in seen:
1971 if unmatch.match(fn):
1972 # If the bbappend or file was already matched by another regex, skip it
1973 # e.g. for a layer within a layer, the outer regex could match, the inner
1974 # regex may match nothing and we should warn about that
1975 if already_matched(fn):
1976 continue
1977 unmatched_regex.remove(unmatch)
1978 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001979
1980 for collection, pattern, regex, _ in self.bbfile_config_priorities:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001981 if regex in unmatched_regex:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001982 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Andrew Geissler5a43b432020-06-13 10:46:56 -05001983 collectlog.warning("No bb files in %s matched BBFILE_PATTERN_%s '%s'" % (self.mc if self.mc else 'default',
1984 collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001985
1986 return priorities
1987
1988class ParsingFailure(Exception):
1989 def __init__(self, realexception, recipe):
1990 self.realexception = realexception
1991 self.recipe = recipe
1992 Exception.__init__(self, realexception, recipe)
1993
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001994class Parser(multiprocessing.Process):
Andrew Geissler9aee5002022-03-30 16:27:02 +00001995 def __init__(self, jobs, results, quit, profile):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001996 self.jobs = jobs
1997 self.results = results
1998 self.quit = quit
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001999 multiprocessing.Process.__init__(self)
2000 self.context = bb.utils.get_context().copy()
2001 self.handlers = bb.event.get_class_handlers().copy()
2002 self.profile = profile
Andrew Geissler9aee5002022-03-30 16:27:02 +00002003 self.queue_signals = False
2004 self.signal_received = []
2005 self.signal_threadlock = threading.Lock()
2006
2007 def catch_sig(self, signum, frame):
2008 if self.queue_signals:
2009 self.signal_received.append(signum)
2010 else:
2011 self.handle_sig(signum, frame)
2012
2013 def handle_sig(self, signum, frame):
2014 if signum == signal.SIGTERM:
2015 signal.signal(signal.SIGTERM, signal.SIG_DFL)
2016 os.kill(os.getpid(), signal.SIGTERM)
2017 elif signum == signal.SIGINT:
2018 signal.default_int_handler(signum, frame)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002019
2020 def run(self):
2021
2022 if not self.profile:
2023 self.realrun()
2024 return
2025
2026 try:
2027 import cProfile as profile
2028 except:
2029 import profile
2030 prof = profile.Profile()
2031 try:
2032 profile.Profile.runcall(prof, self.realrun)
2033 finally:
2034 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
2035 prof.dump_stats(logfile)
2036
2037 def realrun(self):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002038 # Signal handling here is hard. We must not terminate any process or thread holding the write
2039 # lock for the event stream as it will not be released, ever, and things will hang.
2040 # Python handles signals in the main thread/process but they can be raised from any thread and
2041 # we want to defer processing of any SIGTERM/SIGINT signal until we're outside the critical section
2042 # and don't hold the lock (see server/process.py). We therefore always catch the signals (so any
2043 # new thread should also do so) and we defer handling but we handle with the local thread lock
2044 # held (a threading lock, not a multiprocessing one) so that no other thread in the process
2045 # can be in the critical section.
2046 signal.signal(signal.SIGTERM, self.catch_sig)
2047 signal.signal(signal.SIGHUP, signal.SIG_DFL)
2048 signal.signal(signal.SIGINT, self.catch_sig)
2049 bb.utils.set_process_name(multiprocessing.current_process().name)
2050 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2051 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002052
2053 pending = []
Andrew Geissler517393d2023-01-13 08:55:19 -06002054 havejobs = True
Andrew Geissler9aee5002022-03-30 16:27:02 +00002055 try:
Andrew Geissler517393d2023-01-13 08:55:19 -06002056 while havejobs or pending:
2057 if self.quit.is_set():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002058 break
Andrew Geissler9aee5002022-03-30 16:27:02 +00002059
Andrew Geissler517393d2023-01-13 08:55:19 -06002060 job = None
2061 try:
2062 job = self.jobs.pop()
2063 except IndexError:
2064 havejobs = False
2065 if job:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002066 result = self.parse(*job)
2067 # Clear the siggen cache after parsing to control memory usage, its huge
2068 bb.parse.siggen.postparsing_clean_cache()
Andrew Geissler9aee5002022-03-30 16:27:02 +00002069 pending.append(result)
Andrew Geissler517393d2023-01-13 08:55:19 -06002070
2071 if pending:
2072 try:
2073 result = pending.pop()
2074 self.results.put(result, timeout=0.05)
2075 except queue.Full:
2076 pending.append(result)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002077 finally:
2078 self.results.close()
2079 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002080
Patrick Williamse760df82023-05-26 11:10:49 -05002081 def parse(self, mc, cache, filename, appends, layername):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002082 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002083 origfilter = bb.event.LogHandler.filter
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002084 # Record the filename we're parsing into any events generated
2085 def parse_filter(self, record):
2086 record.taskpid = bb.event.worker_pid
2087 record.fn = filename
2088 return True
2089
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002090 # Reset our environment and handlers to the original settings
2091 bb.utils.set_context(self.context.copy())
2092 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002093 bb.event.LogHandler.filter = parse_filter
2094
Patrick Williamse760df82023-05-26 11:10:49 -05002095 return True, mc, cache.parse(filename, appends, layername)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002096 except Exception as exc:
2097 tb = sys.exc_info()[2]
2098 exc.recipe = filename
2099 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002100 return True, None, exc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002101 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
2102 # and for example a worker thread doesn't just exit on its own in response to
2103 # a SystemExit event for example.
2104 except BaseException as exc:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002105 return True, None, ParsingFailure(exc, filename)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002106 finally:
2107 bb.event.LogHandler.filter = origfilter
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002108
2109class CookerParser(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002110 def __init__(self, cooker, mcfilelist, masked):
2111 self.mcfilelist = mcfilelist
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002112 self.cooker = cooker
2113 self.cfgdata = cooker.data
2114 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002115 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002116
2117 # Accounting statistics
2118 self.parsed = 0
2119 self.cached = 0
2120 self.error = 0
2121 self.masked = masked
2122
2123 self.skipped = 0
2124 self.virtuals = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002125
2126 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002127 self.process_names = []
2128
Andrew Geissler5a43b432020-06-13 10:46:56 -05002129 self.bb_caches = bb.cache.MulticonfigCache(self.cfgbuilder, self.cfghash, cooker.caches_array)
2130 self.fromcache = set()
2131 self.willparse = set()
2132 for mc in self.cooker.multiconfigs:
2133 for filename in self.mcfilelist[mc]:
2134 appends = self.cooker.collections[mc].get_file_appends(filename)
Patrick Williamse760df82023-05-26 11:10:49 -05002135 layername = self.cooker.collections[mc].calc_bbfile_priority(filename)[2]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002136 if not self.bb_caches[mc].cacheValid(filename, appends):
Patrick Williamse760df82023-05-26 11:10:49 -05002137 self.willparse.add((mc, self.bb_caches[mc], filename, appends, layername))
Andrew Geissler5a43b432020-06-13 10:46:56 -05002138 else:
Patrick Williamse760df82023-05-26 11:10:49 -05002139 self.fromcache.add((mc, self.bb_caches[mc], filename, appends, layername))
Andrew Geissler5a43b432020-06-13 10:46:56 -05002140
2141 self.total = len(self.fromcache) + len(self.willparse)
2142 self.toparse = len(self.willparse)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002143 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002144
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002145 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Andrew Geissler5a43b432020-06-13 10:46:56 -05002146 multiprocessing.cpu_count()), self.toparse)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002147
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002148 bb.cache.SiggenRecipeInfo.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002149 self.start()
2150 self.haveshutdown = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002151 self.syncthread = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002152
2153 def start(self):
2154 self.results = self.load_cached()
2155 self.processes = []
2156 if self.toparse:
2157 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002158
Andrew Geissler517393d2023-01-13 08:55:19 -06002159 self.parser_quit = multiprocessing.Event()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002160 self.result_queue = multiprocessing.Queue()
Brad Bishop19323692019-04-05 15:28:33 -04002161
2162 def chunkify(lst,n):
2163 return [lst[i::n] for i in range(n)]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002164 self.jobs = chunkify(list(self.willparse), self.num_processes)
Brad Bishop19323692019-04-05 15:28:33 -04002165
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002166 for i in range(0, self.num_processes):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002167 parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, self.cooker.configuration.profile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002168 parser.start()
2169 self.process_names.append(parser.name)
2170 self.processes.append(parser)
2171
2172 self.results = itertools.chain(self.results, self.parse_generator())
2173
Patrick Williams8e7b46e2023-05-01 14:19:06 -05002174 def shutdown(self, clean=True, eventmsg="Parsing halted due to errors"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002175 if not self.toparse:
2176 return
2177 if self.haveshutdown:
2178 return
2179 self.haveshutdown = True
2180
2181 if clean:
2182 event = bb.event.ParseCompleted(self.cached, self.parsed,
2183 self.skipped, self.masked,
2184 self.virtuals, self.error,
2185 self.total)
2186
2187 bb.event.fire(event, self.cfgdata)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002188 else:
Patrick Williams8e7b46e2023-05-01 14:19:06 -05002189 bb.event.fire(bb.event.ParseError(eventmsg), self.cfgdata)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002190 bb.error("Parsing halted due to errors, see error messages above")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002191
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002192 # Cleanup the queue before call process.join(), otherwise there might be
2193 # deadlocks.
2194 while True:
2195 try:
2196 self.result_queue.get(timeout=0.25)
2197 except queue.Empty:
2198 break
2199
Andrew Geissler517393d2023-01-13 08:55:19 -06002200 def sync_caches():
2201 for c in self.bb_caches.values():
2202 bb.cache.SiggenRecipeInfo.reset()
2203 c.sync()
2204
2205 self.syncthread = threading.Thread(target=sync_caches, name="SyncThread")
2206 self.syncthread.start()
2207
2208 self.parser_quit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002209
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002210 for process in self.processes:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002211 process.join(0.5)
2212
2213 for process in self.processes:
2214 if process.exitcode is None:
2215 os.kill(process.pid, signal.SIGINT)
2216
2217 for process in self.processes:
2218 process.join(0.5)
2219
2220 for process in self.processes:
2221 if process.exitcode is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002222 process.terminate()
Andrew Geissler9aee5002022-03-30 16:27:02 +00002223
2224 for process in self.processes:
2225 process.join()
2226 # Added in 3.7, cleans up zombies
2227 if hasattr(process, "close"):
2228 process.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002229
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002230 bb.codeparser.parser_cache_save()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002231 bb.codeparser.parser_cache_savemerge()
Andrew Geissler517393d2023-01-13 08:55:19 -06002232 bb.cache.SiggenRecipeInfo.reset()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002233 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002234 if self.cooker.configuration.profile:
2235 profiles = []
2236 for i in self.process_names:
2237 logfile = "profile-parse-%s.log" % i
2238 if os.path.exists(logfile):
2239 profiles.append(logfile)
2240
2241 pout = "profile-parse.log.processed"
2242 bb.utils.process_profilelog(profiles, pout = pout)
2243 print("Processed parsing statistics saved to %s" % (pout))
2244
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002245 def final_cleanup(self):
2246 if self.syncthread:
2247 self.syncthread.join()
2248
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002249 def load_cached(self):
Patrick Williamse760df82023-05-26 11:10:49 -05002250 for mc, cache, filename, appends, layername in self.fromcache:
Andrew Geissler517393d2023-01-13 08:55:19 -06002251 infos = cache.loadCached(filename, appends)
2252 yield False, mc, infos
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002253
2254 def parse_generator(self):
Andrew Geissler595f6302022-01-24 19:11:47 +00002255 empty = False
2256 while self.processes or not empty:
2257 for process in self.processes.copy():
2258 if not process.is_alive():
2259 process.join()
2260 self.processes.remove(process)
2261
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002262 if self.parsed >= self.toparse:
2263 break
2264
2265 try:
2266 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002267 except queue.Empty:
Andrew Geissler595f6302022-01-24 19:11:47 +00002268 empty = True
Andrew Geissler9aee5002022-03-30 16:27:02 +00002269 yield None, None, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002270 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002271 empty = False
Andrew Geissler9aee5002022-03-30 16:27:02 +00002272 yield result
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002273
Andrew Geissler595f6302022-01-24 19:11:47 +00002274 if not (self.parsed >= self.toparse):
2275 raise bb.parse.ParseError("Not all recipes parsed, parser thread killed/died? Exiting.", None)
2276
2277
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002278 def parse_next(self):
2279 result = []
2280 parsed = None
2281 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002282 parsed, mc, result = next(self.results)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002283 if isinstance(result, BaseException):
2284 # Turn exceptions back into exceptions
2285 raise result
2286 if parsed is None:
2287 # Timeout, loop back through the main loop
2288 return True
2289
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002290 except StopIteration:
2291 self.shutdown()
2292 return False
2293 except bb.BBHandledException as exc:
2294 self.error += 1
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002295 logger.debug('Failed to parse recipe: %s' % exc.recipe)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002296 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002297 return False
2298 except ParsingFailure as exc:
2299 self.error += 1
2300 logger.error('Unable to parse %s: %s' %
2301 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002302 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002303 return False
2304 except bb.parse.ParseError as exc:
2305 self.error += 1
2306 logger.error(str(exc))
Patrick Williams8e7b46e2023-05-01 14:19:06 -05002307 self.shutdown(clean=False, eventmsg=str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002308 return False
2309 except bb.data_smart.ExpansionError as exc:
2310 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002311 bbdir = os.path.dirname(__file__) + os.sep
2312 etype, value, _ = sys.exc_info()
2313 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2314 logger.error('ExpansionError during parsing %s', value.recipe,
2315 exc_info=(etype, value, tb))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002316 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002317 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002318 except Exception as exc:
2319 self.error += 1
2320 etype, value, tb = sys.exc_info()
2321 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002322 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002323 exc_info=(etype, value, exc.traceback))
2324 else:
2325 # Most likely, an exception occurred during raising an exception
2326 import traceback
2327 logger.error('Exception during parse: %s' % traceback.format_exc())
Andrew Geissler9aee5002022-03-30 16:27:02 +00002328 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002329 return False
2330
2331 self.current += 1
2332 self.virtuals += len(result)
2333 if parsed:
2334 self.parsed += 1
2335 if self.parsed % self.progress_chunk == 0:
2336 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2337 self.cfgdata)
2338 else:
2339 self.cached += 1
2340
2341 for virtualfn, info_array in result:
2342 if info_array[0].skipped:
2343 self.skipped += 1
2344 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Andrew Geissler5a43b432020-06-13 10:46:56 -05002345 self.bb_caches[mc].add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002346 parsed=parsed, watcher = self.cooker.add_filewatch)
2347 return True
2348
2349 def reparse(self, filename):
Andrew Geissler517393d2023-01-13 08:55:19 -06002350 bb.cache.SiggenRecipeInfo.reset()
Andrew Geissler5a43b432020-06-13 10:46:56 -05002351 to_reparse = set()
2352 for mc in self.cooker.multiconfigs:
Patrick Williamse760df82023-05-26 11:10:49 -05002353 layername = self.cooker.collections[mc].calc_bbfile_priority(filename)[2]
2354 to_reparse.add((mc, filename, self.cooker.collections[mc].get_file_appends(filename), layername))
Andrew Geissler5a43b432020-06-13 10:46:56 -05002355
Patrick Williamse760df82023-05-26 11:10:49 -05002356 for mc, filename, appends, layername in to_reparse:
2357 infos = self.bb_caches[mc].parse(filename, appends, layername)
Andrew Geissler5a43b432020-06-13 10:46:56 -05002358 for vfn, info_array in infos:
2359 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)