blob: c5bfef55d67e077865e13d6d91a8c6bc11a4c1d8 [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):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600105 self.cooker = cooker
106 self.eventfile = eventfile
107 self.event_queue = []
108
Patrick Williamsda295312023-12-05 16:48:56 -0600109 def write_variables(self):
110 with open(self.eventfile, "a") as f:
111 f.write("%s\n" % json.dumps({ "allvariables" : self.cooker.getAllKeysWithFlags(["doc", "func"])}))
112
113 def send(self, event):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600114 with open(self.eventfile, "a") as f:
115 try:
116 str_event = codecs.encode(pickle.dumps(event), 'base64').decode('utf-8')
117 f.write("%s\n" % json.dumps({"class": event.__module__ + "." + event.__class__.__name__,
118 "vars": str_event}))
119 except Exception as err:
120 import traceback
121 print(err, traceback.format_exc())
122
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600123
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500124#============================================================================#
125# BBCooker
126#============================================================================#
127class BBCooker:
128 """
129 Manages one bitbake build run
130 """
131
Andrew Geissler517393d2023-01-13 08:55:19 -0600132 def __init__(self, featureSet=None, server=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600133 self.recipecaches = None
Andrew Geissler028142b2023-05-05 11:29:21 -0500134 self.baseconfig_valid = False
135 self.parsecache_valid = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500136 self.eventlog = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500137 self.skiplist = {}
138 self.featureset = CookerFeatures()
139 if featureSet:
140 for f in featureSet:
141 self.featureset.setFeature(f)
142
Patrick Williams45852732022-04-02 08:58:32 -0500143 self.orig_syspath = sys.path.copy()
144 self.orig_sysmodules = [*sys.modules]
145
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500146 self.configuration = bb.cookerdata.CookerConfiguration()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147
Andrew Geissler517393d2023-01-13 08:55:19 -0600148 self.process_server = server
149 self.idleCallBackRegister = None
150 self.waitIdle = None
151 if server:
152 self.idleCallBackRegister = server.register_idle_function
153 self.waitIdle = server.wait_for_idle
Andrew Geissler635e0e42020-08-21 15:58:33 -0500154
Brad Bishopf058f492019-01-28 23:50:33 -0500155 bb.debug(1, "BBCooker starting %s" % time.time())
Brad Bishopf058f492019-01-28 23:50:33 -0500156
Andrew Geissler220dafd2023-10-04 10:18:08 -0500157 self.configwatched = {}
158 self.parsewatched = {}
Brad Bishopf058f492019-01-28 23:50:33 -0500159
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500160 # If being called by something like tinfoil, we need to clean cached data
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500161 # which may now be invalid
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500162 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500163 bb.parse.BBHandler.cached_statements = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500164
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500165 self.ui_cmdline = None
Brad Bishop08902b02019-08-20 09:16:51 -0400166 self.hashserv = None
Brad Bishopa34c0302019-09-23 22:34:48 -0400167 self.hashservaddr = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500168
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500169 # TOSTOP must not be set or our children will hang when they output
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600170 try:
171 fd = sys.stdout.fileno()
172 if os.isatty(fd):
173 import termios
174 tcattr = termios.tcgetattr(fd)
175 if tcattr[3] & termios.TOSTOP:
176 buildlog.info("The terminal had the TOSTOP bit set, clearing...")
177 tcattr[3] = tcattr[3] & ~termios.TOSTOP
178 termios.tcsetattr(fd, termios.TCSANOW, tcattr)
179 except UnsupportedOperation:
180 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500181
Andrew Geissler517393d2023-01-13 08:55:19 -0600182 self.command = bb.command.Command(self, self.process_server)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500183 self.state = state.initial
184
185 self.parser = None
186
187 signal.signal(signal.SIGTERM, self.sigterm_exception)
188 # Let SIGHUP exit as SIGTERM
189 signal.signal(signal.SIGHUP, self.sigterm_exception)
190
Brad Bishopf058f492019-01-28 23:50:33 -0500191 bb.debug(1, "BBCooker startup complete %s" % time.time())
Andrew Geissler517393d2023-01-13 08:55:19 -0600192
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500193 def init_configdata(self):
194 if not hasattr(self, "data"):
195 self.initConfigurationData()
196 bb.debug(1, "BBCooker parsed base configuration %s" % time.time())
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500197 self.handlePRServ()
198
Andrew Geissler028142b2023-05-05 11:29:21 -0500199 def _baseconfig_set(self, value):
200 if value and not self.baseconfig_valid:
201 bb.server.process.serverlog("Base config valid")
202 elif not value and self.baseconfig_valid:
203 bb.server.process.serverlog("Base config invalidated")
204 self.baseconfig_valid = value
205
206 def _parsecache_set(self, value):
207 if value and not self.parsecache_valid:
208 bb.server.process.serverlog("Parse cache valid")
209 elif not value and self.parsecache_valid:
210 bb.server.process.serverlog("Parse cache invalidated")
211 self.parsecache_valid = value
212
Andrew Geissler220dafd2023-10-04 10:18:08 -0500213 def add_filewatch(self, deps, configwatcher=False):
214 if configwatcher:
215 watcher = self.configwatched
216 else:
217 watcher = self.parsewatched
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500218
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500219 for i in deps:
Andrew Geissler220dafd2023-10-04 10:18:08 -0500220 f = i[0]
221 mtime = i[1]
222 watcher[f] = mtime
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600223
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500224 def sigterm_exception(self, signum, stackframe):
225 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500226 bb.warn("Cooker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500227 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500228 bb.warn("Cooker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500229 self.state = state.forceshutdown
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600230 bb.event._should_exit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500231
232 def setFeatures(self, features):
233 # we only accept a new feature set if we're in state initial, so we can reset without problems
234 if not self.state in [state.initial, state.shutdown, state.forceshutdown, state.stopped, state.error]:
235 raise Exception("Illegal state for feature set change")
236 original_featureset = list(self.featureset)
237 for feature in features:
238 self.featureset.setFeature(feature)
239 bb.debug(1, "Features set %s (was %s)" % (original_featureset, list(self.featureset)))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500240 if (original_featureset != list(self.featureset)) and self.state != state.error and hasattr(self, "data"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500241 self.reset()
242
243 def initConfigurationData(self):
244
245 self.state = state.initial
246 self.caches_array = []
247
Patrick Williams45852732022-04-02 08:58:32 -0500248 sys.path = self.orig_syspath.copy()
249 for mod in [*sys.modules]:
250 if mod not in self.orig_sysmodules:
251 del sys.modules[mod]
252
Andrew Geissler220dafd2023-10-04 10:18:08 -0500253 self.configwatched = {}
Patrick Williamsde0582f2022-04-08 10:23:27 -0500254
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500255 # Need to preserve BB_CONSOLELOG over resets
256 consolelog = None
257 if hasattr(self, "data"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500258 consolelog = self.data.getVar("BB_CONSOLELOG")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500259
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500260 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
261 self.enableDataTracking()
262
Andrew Geissler517393d2023-01-13 08:55:19 -0600263 caches_name_array = ['bb.cache:CoreRecipeInfo']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500264 # We hardcode all known cache types in a single place, here.
265 if CookerFeatures.HOB_EXTRA_CACHES in self.featureset:
Andrew Geissler517393d2023-01-13 08:55:19 -0600266 caches_name_array.append("bb.cache_extra:HobRecipeInfo")
267 if CookerFeatures.RECIPE_SIGGEN_INFO in self.featureset:
268 caches_name_array.append("bb.cache:SiggenRecipeInfo")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500269
270 # At least CoreRecipeInfo will be loaded, so caches_array will never be empty!
271 # This is the entry point, no further check needed!
272 for var in caches_name_array:
273 try:
274 module_name, cache_name = var.split(':')
275 module = __import__(module_name, fromlist=(cache_name,))
276 self.caches_array.append(getattr(module, cache_name))
277 except ImportError as exc:
278 logger.critical("Unable to import extra RecipeInfo '%s' from '%s': %s" % (cache_name, module_name, exc))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500279 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500280
281 self.databuilder = bb.cookerdata.CookerDataBuilder(self.configuration, False)
282 self.databuilder.parseBaseConfiguration()
283 self.data = self.databuilder.data
284 self.data_hash = self.databuilder.data_hash
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500285 self.extraconfigdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500286
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600287 eventlog = self.data.getVar("BB_DEFAULT_EVENTLOG")
288 if not self.configuration.writeeventlog and eventlog:
289 self.setupEventLog(eventlog)
290
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500291 if consolelog:
292 self.data.setVar("BB_CONSOLELOG", consolelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500293
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500294 self.data.setVar('BB_CMDLINE', self.ui_cmdline)
295
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500296 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
297 self.disableDataTracking()
298
Brad Bishop15ae2502019-06-18 21:44:24 -0400299 for mc in self.databuilder.mcdata.values():
Andrew Geissler220dafd2023-10-04 10:18:08 -0500300 self.add_filewatch(mc.getVar("__base_depends", False), configwatcher=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500301
Andrew Geissler028142b2023-05-05 11:29:21 -0500302 self._baseconfig_set(True)
303 self._parsecache_set(False)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500304
305 def handlePRServ(self):
306 # Setup a PR Server based on the new configuration
307 try:
308 self.prhost = prserv.serv.auto_start(self.data)
309 except prserv.serv.PRServiceConfigError as e:
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500310 bb.fatal("Unable to start PR Server, exiting, check the bitbake-cookerdaemon.log")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311
Brad Bishopa34c0302019-09-23 22:34:48 -0400312 if self.data.getVar("BB_HASHSERVE") == "auto":
313 # Create a new hash server bound to a unix domain socket
Brad Bishop08902b02019-08-20 09:16:51 -0400314 if not self.hashserv:
315 dbfile = (self.data.getVar("PERSISTENT_DIR") or self.data.getVar("CACHE")) + "/hashserv.db"
Andrew Geissler595f6302022-01-24 19:11:47 +0000316 upstream = self.data.getVar("BB_HASHSERVE_UPSTREAM") or None
317 if upstream:
318 import socket
319 try:
320 sock = socket.create_connection(upstream.split(":"), 5)
321 sock.close()
322 except socket.error as e:
Andrew Geissler87f5cff2022-09-30 13:13:31 -0500323 bb.warn("BB_HASHSERVE_UPSTREAM is not valid, unable to connect hash equivalence server at '%s': %s"
Andrew Geissler595f6302022-01-24 19:11:47 +0000324 % (upstream, repr(e)))
325
Brad Bishopa34c0302019-09-23 22:34:48 -0400326 self.hashservaddr = "unix://%s/hashserve.sock" % self.data.getVar("TOPDIR")
Andrew Geissler5199d832021-09-24 16:47:35 -0500327 self.hashserv = hashserv.create_server(
328 self.hashservaddr,
329 dbfile,
330 sync=False,
Andrew Geissler595f6302022-01-24 19:11:47 +0000331 upstream=upstream,
Andrew Geissler5199d832021-09-24 16:47:35 -0500332 )
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600333 self.hashserv.serve_as_process(log_level=logging.WARNING)
Brad Bishop08902b02019-08-20 09:16:51 -0400334 for mc in self.databuilder.mcdata:
Andrew Geissler517393d2023-01-13 08:55:19 -0600335 self.databuilder.mcorigdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishopa34c0302019-09-23 22:34:48 -0400336 self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400337
338 bb.parse.init_parser(self.data)
339
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500340 def enableDataTracking(self):
341 self.configuration.tracking = True
342 if hasattr(self, "data"):
343 self.data.enableTracking()
344
345 def disableDataTracking(self):
346 self.configuration.tracking = False
347 if hasattr(self, "data"):
348 self.data.disableTracking()
349
Andrew Geissler220dafd2023-10-04 10:18:08 -0500350 def revalidateCaches(self):
351 bb.parse.clear_cache()
352
353 clean = True
354 for f in self.configwatched:
355 if not bb.parse.check_mtime(f, self.configwatched[f]):
356 bb.server.process.serverlog("Found %s changed, invalid cache" % f)
357 self._baseconfig_set(False)
358 self._parsecache_set(False)
359 clean = False
360 break
361
362 if clean:
363 for f in self.parsewatched:
364 if not bb.parse.check_mtime(f, self.parsewatched[f]):
365 bb.server.process.serverlog("Found %s changed, invalid cache" % f)
366 self._parsecache_set(False)
367 clean = False
368 break
369
370 if not clean:
371 bb.parse.BBHandler.cached_statements = {}
372
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500373 def parseConfiguration(self):
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600374 self.updateCacheSync()
375
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500376 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500377 nice = self.data.getVar("BB_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500378 if nice:
379 curnice = os.nice(0)
380 nice = int(nice) - curnice
381 buildlog.verbose("Renice to %s " % os.nice(nice))
382
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600383 if self.recipecaches:
384 del self.recipecaches
385 self.multiconfigs = self.databuilder.mcdata.keys()
386 self.recipecaches = {}
387 for mc in self.multiconfigs:
388 self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500389
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500390 self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
Patrick Williamse760df82023-05-26 11:10:49 -0500391 self.collections = {}
392 for mc in self.multiconfigs:
393 self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500394
Andrew Geissler028142b2023-05-05 11:29:21 -0500395 self._parsecache_set(False)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500396
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600397 def setupEventLog(self, eventlog):
398 if self.eventlog and self.eventlog[0] != eventlog:
399 bb.event.unregister_UIHhandler(self.eventlog[1])
Patrick Williamsda295312023-12-05 16:48:56 -0600400 self.eventlog = None
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600401 if not self.eventlog or self.eventlog[0] != eventlog:
402 # we log all events to a file if so directed
403 # register the log file writer as UI Handler
404 if not os.path.exists(os.path.dirname(eventlog)):
405 bb.utils.mkdirhier(os.path.dirname(eventlog))
406 writer = EventWriter(self, eventlog)
407 EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
Patrick Williamsda295312023-12-05 16:48:56 -0600408 self.eventlog = (eventlog, bb.event.register_UIHhandler(EventLogWriteHandler(writer)), writer)
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600409
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500410 def updateConfigOpts(self, options, environment, cmdline):
411 self.ui_cmdline = cmdline
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500412 clean = True
413 for o in options:
414 if o in ['prefile', 'postfile']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500415 # Only these options may require a reparse
416 try:
417 if getattr(self.configuration, o) == options[o]:
418 # Value is the same, no need to mark dirty
419 continue
420 except AttributeError:
421 pass
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600422 logger.debug("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500423 print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500424 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500425 if hasattr(self.configuration, o):
426 setattr(self.configuration, o, options[o])
427
428 if self.configuration.writeeventlog:
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600429 self.setupEventLog(self.configuration.writeeventlog)
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500430
431 bb.msg.loggerDefaultLogLevel = self.configuration.default_loglevel
432 bb.msg.loggerDefaultDomains = self.configuration.debug_domains
433
434 if hasattr(self, "data"):
435 origenv = bb.data.init()
436 for k in environment:
437 origenv.setVar(k, environment[k])
438 self.data.setVar("BB_ORIGENV", origenv)
439
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500440 for k in bb.utils.approved_variables():
441 if k in environment and k not in self.configuration.env:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600442 logger.debug("Updating new environment variable %s to %s" % (k, environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500443 self.configuration.env[k] = environment[k]
444 clean = False
445 if k in self.configuration.env and k not in environment:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600446 logger.debug("Updating environment variable %s (deleted)" % (k))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500447 del self.configuration.env[k]
448 clean = False
449 if k not in self.configuration.env and k not in environment:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500450 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500451 if environment[k] != self.configuration.env[k]:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600452 logger.debug("Updating environment variable %s from %s to %s" % (k, self.configuration.env[k], environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500453 self.configuration.env[k] = environment[k]
454 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500455
456 # Now update all the variables not in the datastore to match
457 self.configuration.env = environment
458
Andrew Geissler220dafd2023-10-04 10:18:08 -0500459 self.revalidateCaches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500460 if not clean:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600461 logger.debug("Base environment change, triggering reparse")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500462 self.reset()
463
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500464 def showVersions(self):
465
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500466 (latest_versions, preferred_versions, required) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500467
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500468 logger.plain("%-35s %25s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version", "Required Version")
469 logger.plain("%-35s %25s %25s %25s\n", "===========", "==============", "=================", "================")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500470
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500471 for p in sorted(self.recipecaches[''].pkg_pn):
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500472 preferred = preferred_versions[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500473 latest = latest_versions[p]
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500474 requiredstr = ""
475 preferredstr = ""
476 if required[p]:
477 if preferred[0] is not None:
478 requiredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
479 else:
480 bb.fatal("REQUIRED_VERSION of package %s not available" % p)
481 else:
482 preferredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500483
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500484 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
485
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500486 if preferred == latest:
487 preferredstr = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500489 logger.plain("%-35s %25s %25s %25s", p, lateststr, preferredstr, requiredstr)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500490
491 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
492 """
493 Show the outer or per-recipe environment
494 """
495 fn = None
496 envdata = None
Brad Bishop15ae2502019-06-18 21:44:24 -0400497 mc = ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500498 if not pkgs_to_build:
499 pkgs_to_build = []
500
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500501 orig_tracking = self.configuration.tracking
502 if not orig_tracking:
503 self.enableDataTracking()
504 self.reset()
Andrew Geissler9aee5002022-03-30 16:27:02 +0000505 # reset() resets to the UI requested value so we have to redo this
506 self.enableDataTracking()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500507
Brad Bishop15ae2502019-06-18 21:44:24 -0400508 def mc_base(p):
509 if p.startswith('mc:'):
510 s = p.split(':')
511 if len(s) == 2:
512 return s[1]
513 return None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500514
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500515 if buildfile:
516 # Parse the configuration here. We need to do it explicitly here since
517 # this showEnvironment() code path doesn't use the cache
518 self.parseConfiguration()
519
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600520 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500521 fn = self.matchFile(fn, mc)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600522 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500523 elif len(pkgs_to_build) == 1:
Brad Bishop15ae2502019-06-18 21:44:24 -0400524 mc = mc_base(pkgs_to_build[0])
525 if not mc:
526 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
527 if pkgs_to_build[0] in set(ignore.split()):
528 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500529
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000530 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.halt, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500531
Brad Bishop15ae2502019-06-18 21:44:24 -0400532 mc = runlist[0][0]
533 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534
535 if fn:
536 try:
Patrick Williamse760df82023-05-26 11:10:49 -0500537 layername = self.collections[mc].calc_bbfile_priority(fn)[2]
538 envdata = self.databuilder.parseRecipe(fn, self.collections[mc].get_file_appends(fn), layername)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500539 except Exception as e:
540 parselog.exception("Unable to read %s", fn)
541 raise
Brad Bishop15ae2502019-06-18 21:44:24 -0400542 else:
543 if not mc in self.databuilder.mcdata:
Andrew Geissler5082cc72023-09-11 08:41:39 -0400544 bb.fatal('No multiconfig named "%s" found' % mc)
Brad Bishop15ae2502019-06-18 21:44:24 -0400545 envdata = self.databuilder.mcdata[mc]
546 data.expandKeys(envdata)
547 parse.ast.runAnonFuncs(envdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500548
549 # Display history
550 with closing(StringIO()) as env:
551 self.data.inchistory.emit(env)
552 logger.plain(env.getvalue())
553
554 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500555 with closing(StringIO()) as env:
556 data.emit_env(env, envdata, True)
557 logger.plain(env.getvalue())
558
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000559 # emit the metadata which isn't valid shell
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500560 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600561 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500562 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500563
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500564 if not orig_tracking:
565 self.disableDataTracking()
566 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500567
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000568 def buildTaskData(self, pkgs_to_build, task, halt, allowincomplete=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500569 """
570 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
571 """
572 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
573
574 # A task of None means use the default task
575 if task is None:
576 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500577 if not task.startswith("do_"):
578 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500579
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500580 targetlist = self.checkPackages(pkgs_to_build, task)
581 fulltargetlist = []
582 defaulttask_implicit = ''
583 defaulttask_explicit = False
584 wildcard = False
585
586 # Wild card expansion:
Brad Bishop15ae2502019-06-18 21:44:24 -0400587 # Replace string such as "mc:*:bash"
588 # into "mc:A:bash mc:B:bash bash"
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500589 for k in targetlist:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600590 if k.startswith("mc:") and k.count(':') >= 2:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500591 if wildcard:
592 bb.fatal('multiconfig conflict')
593 if k.split(":")[1] == "*":
594 wildcard = True
595 for mc in self.multiconfigs:
596 if mc:
597 fulltargetlist.append(k.replace('*', mc))
598 # implicit default task
599 else:
600 defaulttask_implicit = k.split(":")[2]
601 else:
602 fulltargetlist.append(k)
603 else:
604 defaulttask_explicit = True
605 fulltargetlist.append(k)
606
607 if not defaulttask_explicit and defaulttask_implicit != '':
608 fulltargetlist.append(defaulttask_implicit)
609
610 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600611 taskdata = {}
612 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500613
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600614 for mc in self.multiconfigs:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000615 taskdata[mc] = bb.taskdata.TaskData(halt, skiplist=self.skiplist, allowincomplete=allowincomplete)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600616 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600617 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500618
619 current = 0
620 runlist = []
621 for k in fulltargetlist:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500622 origk = k
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600623 mc = ""
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600624 if k.startswith("mc:") and k.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600625 mc = k.split(":")[1]
626 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627 ktask = task
628 if ":do_" in k:
629 k2 = k.split(":do_")
630 k = k2[0]
631 ktask = k2[1]
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500632
633 if mc not in self.multiconfigs:
634 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named %s" % (origk, mc))
635
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600636 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500637 current += 1
638 if not ktask.startswith("do_"):
639 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600640 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
641 # e.g. in ASSUME_PROVIDED
642 continue
643 fn = taskdata[mc].build_targets[k][0]
644 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500645 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600646
Brad Bishop15ae2502019-06-18 21:44:24 -0400647 havemc = False
648 for mc in self.multiconfigs:
649 if taskdata[mc].get_mcdepends():
650 havemc = True
Brad Bishopf058f492019-01-28 23:50:33 -0500651
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800652 # No need to do check providers if there are no mcdeps or not an mc build
Brad Bishop15ae2502019-06-18 21:44:24 -0400653 if havemc or len(self.multiconfigs) > 1:
Andrew Geissler99467da2019-02-25 18:54:23 -0600654 seen = set()
655 new = True
656 # Make sure we can provide the multiconfig dependency
657 while new:
658 mcdeps = set()
659 # Add unresolved first, so we can get multiconfig indirect dependencies on time
660 for mc in self.multiconfigs:
661 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
662 mcdeps |= set(taskdata[mc].get_mcdepends())
663 new = False
Patrick Williams03907ee2022-05-01 06:28:52 -0500664 for k in mcdeps:
665 if k in seen:
666 continue
667 l = k.split(':')
668 depmc = l[2]
669 if depmc not in self.multiconfigs:
670 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named configuration %s" % (k,depmc))
671 else:
672 logger.debug("Adding providers for multiconfig dependency %s" % l[3])
673 taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
674 seen.add(k)
675 new = True
Brad Bishopf058f492019-01-28 23:50:33 -0500676
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600677 for mc in self.multiconfigs:
678 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
679
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500680 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600681 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500682
683 def prepareTreeData(self, pkgs_to_build, task):
684 """
685 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
686 """
687
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000688 # We set halt to False here to prevent unbuildable targets raising
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600690 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500691
692 return runlist, taskdata
693
694 ######## WARNING : this function requires cache_extra to be enabled ########
695
696 def generateTaskDepTreeData(self, pkgs_to_build, task):
697 """
698 Create a dependency graph of pkgs_to_build including reverse dependency
699 information.
700 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500701 if not task.startswith("do_"):
702 task = "do_%s" % task
703
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500704 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600705 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500706 rq.rqdata.prepare()
707 return self.buildDependTree(rq, taskdata)
708
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600709 @staticmethod
710 def add_mc_prefix(mc, pn):
711 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -0400712 return "mc:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600713 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500714
715 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600716 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500717 depend_tree = {}
718 depend_tree["depends"] = {}
719 depend_tree["tdepends"] = {}
720 depend_tree["pn"] = {}
721 depend_tree["rdepends-pn"] = {}
722 depend_tree["packages"] = {}
723 depend_tree["rdepends-pkg"] = {}
724 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500725 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600726 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600728 for mc in taskdata:
729 for name, fn in list(taskdata[mc].get_providermap().items()):
730 pn = self.recipecaches[mc].pkg_fn[fn]
731 pn = self.add_mc_prefix(mc, pn)
732 if name != pn:
733 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
734 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500735
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600736 for tid in rq.rqdata.runtaskentries:
737 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
738 pn = self.recipecaches[mc].pkg_fn[taskfn]
739 pn = self.add_mc_prefix(mc, pn)
740 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500741 if pn not in depend_tree["pn"]:
742 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600743 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500744 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600745 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746
747 # if we have extra caches, list all attributes they bring in
748 extra_info = []
749 for cache_class in self.caches_array:
750 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
751 cachefields = getattr(cache_class, 'cachefields', [])
752 extra_info = extra_info + cachefields
753
754 # for all attributes stored, add them to the dependency tree
755 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600756 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500757
758
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500759 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
760 if not dotname in depend_tree["tdepends"]:
761 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600762 for dep in rq.rqdata.runtaskentries[tid].depends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800763 (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
764 deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
Andrew Geissler595f6302022-01-24 19:11:47 +0000765 if depmc:
766 depmc = "mc:" + depmc + ":"
767 depend_tree["tdepends"][dotname].append("%s%s.%s" % (depmc, deppn, bb.runqueue.taskname_from_tid(dep)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600768 if taskfn not in seen_fns:
769 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500770 packages = []
771
772 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600773 for dep in taskdata[mc].depids[taskfn]:
774 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775
776 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600777 for rdep in taskdata[mc].rdepids[taskfn]:
778 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600780 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500781 for package in rdepends:
782 depend_tree["rdepends-pkg"][package] = []
783 for rdepend in rdepends[package]:
784 depend_tree["rdepends-pkg"][package].append(rdepend)
785 packages.append(package)
786
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600787 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500788 for package in rrecs:
789 depend_tree["rrecs-pkg"][package] = []
790 for rdepend in rrecs[package]:
791 depend_tree["rrecs-pkg"][package].append(rdepend)
792 if not package in packages:
793 packages.append(package)
794
795 for package in packages:
796 if package not in depend_tree["packages"]:
797 depend_tree["packages"][package] = {}
798 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600799 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500800 depend_tree["packages"][package]["version"] = version
801
802 return depend_tree
803
804 ######## WARNING : this function requires cache_extra to be enabled ########
805 def generatePkgDepTreeData(self, pkgs_to_build, task):
806 """
807 Create a dependency tree of pkgs_to_build, returning the data.
808 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500809 if not task.startswith("do_"):
810 task = "do_%s" % task
811
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500812 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500813
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600814 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500815 depend_tree = {}
816 depend_tree["depends"] = {}
817 depend_tree["pn"] = {}
818 depend_tree["rdepends-pn"] = {}
819 depend_tree["rdepends-pkg"] = {}
820 depend_tree["rrecs-pkg"] = {}
821
822 # if we have extra caches, list all attributes they bring in
823 extra_info = []
824 for cache_class in self.caches_array:
825 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
826 cachefields = getattr(cache_class, 'cachefields', [])
827 extra_info = extra_info + cachefields
828
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600829 tids = []
830 for mc in taskdata:
831 for tid in taskdata[mc].taskentries:
832 tids.append(tid)
833
834 for tid in tids:
835 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
836
837 pn = self.recipecaches[mc].pkg_fn[taskfn]
838 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500839
840 if pn not in depend_tree["pn"]:
841 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600842 depend_tree["pn"][pn]["filename"] = taskfn
843 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600845 rdepends = self.recipecaches[mc].rundeps[taskfn]
846 rrecs = self.recipecaches[mc].runrecs[taskfn]
847 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500848
849 # for all extra attributes stored, add them to the dependency tree
850 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600851 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500852
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600853 if taskfn not in seen_fns:
854 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500855
856 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500857 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500858 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600859 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
860 fn_provider = taskdata[mc].build_targets[dep][0]
861 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500862 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500863 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600864 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500865 depend_tree["depends"][pn].append(pn_provider)
866
867 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600868 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500869 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600870 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
871 fn_rprovider = taskdata[mc].run_targets[rdep][0]
872 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500873 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600874 pn_rprovider = rdep
875 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500876 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
877
878 depend_tree["rdepends-pkg"].update(rdepends)
879 depend_tree["rrecs-pkg"].update(rrecs)
880
881 return depend_tree
882
883 def generateDepTreeEvent(self, pkgs_to_build, task):
884 """
885 Create a task dependency graph of pkgs_to_build.
886 Generate an event with the result
887 """
888 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
889 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
890
891 def generateDotGraphFiles(self, pkgs_to_build, task):
892 """
893 Create a task dependency graph of pkgs_to_build.
894 Save the result to a set of .dot files.
895 """
896
897 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
898
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500899 with open('pn-buildlist', 'w') as f:
900 for pn in depgraph["pn"]:
901 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500902 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500903
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500904 # Remove old format output files to ensure no confusion with stale data
905 try:
906 os.unlink('pn-depends.dot')
907 except FileNotFoundError:
908 pass
909 try:
910 os.unlink('package-depends.dot')
911 except FileNotFoundError:
912 pass
Brad Bishop79641f22019-09-10 07:20:22 -0400913 try:
914 os.unlink('recipe-depends.dot')
915 except FileNotFoundError:
916 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500917
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500918 with open('task-depends.dot', 'w') as f:
919 f.write("digraph depends {\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400920 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500921 (pn, taskname) = task.rsplit(".", 1)
922 fn = depgraph["pn"][pn]["filename"]
923 version = depgraph["pn"][pn]["version"]
924 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 -0400925 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500926 f.write('"%s" -> "%s"\n' % (task, dep))
927 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500928 logger.info("Task dependencies saved to 'task-depends.dot'")
929
930 def show_appends_with_no_recipes(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500931 appends_without_recipes = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500932 # Determine which bbappends haven't been applied
Andrew Geissler5a43b432020-06-13 10:46:56 -0500933 for mc in self.multiconfigs:
934 # First get list of recipes, including skipped
935 recipefns = list(self.recipecaches[mc].pkg_fn.keys())
936 recipefns.extend(self.skiplist.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500937
Andrew Geissler5a43b432020-06-13 10:46:56 -0500938 # Work out list of bbappends that have been applied
939 applied_appends = []
940 for fn in recipefns:
941 applied_appends.extend(self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500942
Andrew Geissler5a43b432020-06-13 10:46:56 -0500943 appends_without_recipes[mc] = []
944 for _, appendfn in self.collections[mc].bbappends:
945 if not appendfn in applied_appends:
946 appends_without_recipes[mc].append(appendfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500947
Andrew Geissler5a43b432020-06-13 10:46:56 -0500948 msgs = []
949 for mc in sorted(appends_without_recipes.keys()):
950 if appends_without_recipes[mc]:
951 msgs.append('No recipes in %s available for:\n %s' % (mc if mc else 'default',
952 '\n '.join(appends_without_recipes[mc])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500953
Andrew Geissler5a43b432020-06-13 10:46:56 -0500954 if msgs:
955 msg = "\n".join(msgs)
956 warn_only = self.databuilder.mcdata[mc].getVar("BB_DANGLINGAPPENDS_WARNONLY", \
957 False) or "no"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500958 if warn_only.lower() in ("1", "yes", "true"):
959 bb.warn(msg)
960 else:
961 bb.fatal(msg)
962
963 def handlePrefProviders(self):
964
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600965 for mc in self.multiconfigs:
966 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600967 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500968
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600969 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500970 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600971 try:
972 (providee, provider) = p.split(':')
973 except:
974 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
975 continue
976 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
977 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
978 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500979
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500980 def findConfigFilePath(self, configfile):
981 """
982 Find the location on disk of configfile and if it exists and was parsed by BitBake
983 emit the ConfigFilePathFound event with the path to the file.
984 """
985 path = bb.cookerdata.findConfigFile(configfile, self.data)
986 if not path:
987 return
988
989 # Generate a list of parsed configuration files by searching the files
990 # listed in the __depends and __base_depends variables with a .conf suffix.
991 conffiles = []
992 dep_files = self.data.getVar('__base_depends', False) or []
993 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
994
995 for f in dep_files:
996 if f[0].endswith(".conf"):
997 conffiles.append(f[0])
998
999 _, conf, conffile = path.rpartition("conf/")
1000 match = os.path.join(conf, conffile)
1001 # Try and find matches for conf/conffilename.conf as we don't always
1002 # have the full path to the file.
1003 for cfg in conffiles:
1004 if cfg.endswith(match):
1005 bb.event.fire(bb.event.ConfigFilePathFound(path),
1006 self.data)
1007 break
1008
1009 def findFilesMatchingInDir(self, filepattern, directory):
1010 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001011 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001012 'directory' in each BBPATH. i.e. to find all rootfs package classes available
1013 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
1014 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001015 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001016 """
1017
1018 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001019 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001020 for path in bbpaths:
1021 dirpath = os.path.join(path, directory)
1022 if os.path.exists(dirpath):
1023 for root, dirs, files in os.walk(dirpath):
1024 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001025 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001026 matches.append(f)
1027
1028 if matches:
1029 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1030
Patrick Williams93c203f2021-10-06 16:15:23 -05001031 def testCookerCommandEvent(self, filepattern):
1032 # Dummy command used by OEQA selftest to test tinfoil without IO
1033 matches = ["A", "B"]
1034 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1035
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001036 def findProviders(self, mc=''):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001037 return bb.providers.findProviders(self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001038
1039 def findBestProvider(self, pn, mc=''):
1040 if pn in self.recipecaches[mc].providers:
1041 filenames = self.recipecaches[mc].providers[pn]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001042 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.databuilder.mcdata[mc], self.recipecaches[mc])
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001043 if eligible is not None:
1044 filename = eligible[0]
1045 else:
1046 filename = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001047 return None, None, None, filename
1048 elif pn in self.recipecaches[mc].pkg_pn:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001049 (latest, latest_f, preferred_ver, preferred_file, required) = bb.providers.findBestProvider(pn, self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
1050 if required and preferred_file is None:
1051 return None, None, None, None
1052 return (latest, latest_f, preferred_ver, preferred_file)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001053 else:
1054 return None, None, None, None
1055
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001056 def findConfigFiles(self, varname):
1057 """
1058 Find config files which are appropriate values for varname.
1059 i.e. MACHINE, DISTRO
1060 """
1061 possible = []
1062 var = varname.lower()
1063
1064 data = self.data
1065 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001066 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001067 for path in bbpaths:
1068 confpath = os.path.join(path, "conf", var)
1069 if os.path.exists(confpath):
1070 for root, dirs, files in os.walk(confpath):
1071 # get all child files, these are appropriate values
1072 for f in files:
1073 val, sep, end = f.rpartition('.')
1074 if end == 'conf':
1075 possible.append(val)
1076
1077 if possible:
1078 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1079
1080 def findInheritsClass(self, klass):
1081 """
1082 Find all recipes which inherit the specified class
1083 """
1084 pkg_list = []
1085
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001086 for pfn in self.recipecaches[''].pkg_fn:
1087 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001088 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001089 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001090
1091 return pkg_list
1092
1093 def generateTargetsTree(self, klass=None, pkgs=None):
1094 """
1095 Generate a dependency tree of buildable targets
1096 Generate an event with the result
1097 """
1098 # if the caller hasn't specified a pkgs list default to universe
1099 if not pkgs:
1100 pkgs = ['universe']
1101 # if inherited_class passed ensure all recipes which inherit the
1102 # specified class are included in pkgs
1103 if klass:
1104 extra_pkgs = self.findInheritsClass(klass)
1105 pkgs = pkgs + extra_pkgs
1106
1107 # generate a dependency tree for all our packages
1108 tree = self.generatePkgDepTreeData(pkgs, 'build')
1109 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1110
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001111 def interactiveMode( self ):
1112 """Drop off into a shell"""
1113 try:
1114 from bb import shell
1115 except ImportError:
1116 parselog.exception("Interactive mode not available")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001117 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001118 else:
1119 shell.start( self )
1120
1121
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001122 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001123 """Handle collections"""
1124 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001125 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001126 if collections:
1127 collection_priorities = {}
1128 collection_depends = {}
1129 collection_list = collections.split()
1130 min_prio = 0
1131 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001132 bb.debug(1,'Processing %s in collection list' % (c))
1133
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001134 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001135 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001136 if priority:
1137 try:
1138 prio = int(priority)
1139 except ValueError:
1140 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1141 errors = True
1142 if min_prio == 0 or prio < min_prio:
1143 min_prio = prio
1144 collection_priorities[c] = prio
1145 else:
1146 collection_priorities[c] = None
1147
1148 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001149 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001150 if deps:
1151 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001152 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001153 except bb.utils.VersionStringException as vse:
1154 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001155 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001156 if dep in collection_list:
1157 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001158 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001159 (op, depver) = opstr.split()
1160 if layerver:
1161 try:
1162 res = bb.utils.vercmp_string_op(layerver, depver, op)
1163 except bb.utils.VersionStringException as vse:
1164 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1165 if not res:
1166 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)
1167 errors = True
1168 else:
1169 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)
1170 errors = True
1171 else:
1172 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1173 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001174 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001175 else:
1176 collection_depends[c] = []
1177
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001178 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001179 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001180 if recs:
1181 try:
1182 recDict = bb.utils.explode_dep_versions2(recs)
1183 except bb.utils.VersionStringException as vse:
1184 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1185 for rec, oplist in list(recDict.items()):
1186 if rec in collection_list:
1187 if oplist:
1188 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001189 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001190 if layerver:
1191 (op, recver) = opstr.split()
1192 try:
1193 res = bb.utils.vercmp_string_op(layerver, recver, op)
1194 except bb.utils.VersionStringException as vse:
1195 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1196 if not res:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001197 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 -06001198 continue
1199 else:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001200 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 -06001201 continue
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001202 parselog.debug3("Layer '%s' recommends layer '%s', so we are adding it", c, rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001203 collection_depends[c].append(rec)
1204 else:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001205 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 -06001206
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001207 # Recursively work out collection priorities based on dependencies
1208 def calc_layer_priority(collection):
1209 if not collection_priorities[collection]:
1210 max_depprio = min_prio
1211 for dep in collection_depends[collection]:
1212 calc_layer_priority(dep)
1213 depprio = collection_priorities[dep]
1214 if depprio > max_depprio:
1215 max_depprio = depprio
1216 max_depprio += 1
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001217 parselog.debug("Calculated priority of layer %s as %d", collection, max_depprio)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001218 collection_priorities[collection] = max_depprio
1219
1220 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1221 for c in collection_list:
1222 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001223 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001224 if regex is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001225 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1226 errors = True
1227 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001228 elif regex == "":
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001229 parselog.debug("BBFILE_PATTERN_%s is empty" % c)
Brad Bishop19323692019-04-05 15:28:33 -04001230 cre = re.compile('^NULL$')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001231 errors = False
1232 else:
1233 try:
1234 cre = re.compile(regex)
1235 except re.error:
1236 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1237 errors = True
1238 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001239 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001240 if errors:
1241 # We've already printed the actual error(s)
1242 raise CollectionError("Errors during parsing layer configuration")
1243
1244 def buildSetVars(self):
1245 """
1246 Setup any variables needed before starting a build
1247 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001248 t = time.gmtime()
1249 for mc in self.databuilder.mcdata:
1250 ds = self.databuilder.mcdata[mc]
1251 if not ds.getVar("BUILDNAME", False):
1252 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1253 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1254 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1255 ds.setVar("TIME", time.strftime('%H%M%S', t))
1256
1257 def reset_mtime_caches(self):
1258 """
1259 Reset mtime caches - this is particularly important when memory resident as something
1260 which is cached is not unlikely to have changed since the last invocation (e.g. a
1261 file associated with a recipe might have been modified by the user).
1262 """
1263 build.reset_cache()
1264 bb.fetch._checksum_cache.mtime_cache.clear()
1265 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1266 if siggen_cache:
1267 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001268
Andrew Geissler5a43b432020-06-13 10:46:56 -05001269 def matchFiles(self, bf, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001270 """
1271 Find the .bb files which match the expression in 'buildfile'.
1272 """
1273 if bf.startswith("/") or bf.startswith("../"):
1274 bf = os.path.abspath(bf)
1275
Patrick Williamse760df82023-05-26 11:10:49 -05001276 collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)}
1277 filelist, masked, searchdirs = collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001278 try:
1279 os.stat(bf)
1280 bf = os.path.abspath(bf)
1281 return [bf]
1282 except OSError:
1283 regexp = re.compile(bf)
1284 matches = []
1285 for f in filelist:
1286 if regexp.search(f) and os.path.isfile(f):
1287 matches.append(f)
1288 return matches
1289
Andrew Geissler5a43b432020-06-13 10:46:56 -05001290 def matchFile(self, buildfile, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001291 """
1292 Find the .bb file which matches the expression in 'buildfile'.
1293 Raise an error if multiple files
1294 """
Andrew Geissler5a43b432020-06-13 10:46:56 -05001295 matches = self.matchFiles(buildfile, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001296 if len(matches) != 1:
1297 if matches:
1298 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1299 if matches:
1300 for f in matches:
1301 msg += "\n %s" % f
1302 parselog.error(msg)
1303 else:
1304 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1305 raise NoSpecificMatch
1306 return matches[0]
1307
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001308 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001309 """
1310 Build the file matching regexp buildfile
1311 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001312 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001313
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001314 # Too many people use -b because they think it's how you normally
1315 # specify a target to be built, so show a warning
1316 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1317
1318 self.buildFileInternal(buildfile, task)
1319
1320 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1321 """
1322 Build the file matching regexp buildfile
1323 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001324
1325 # Parse the configuration here. We need to do it explicitly here since
1326 # buildFile() doesn't use the cache
1327 self.parseConfiguration()
1328
1329 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001330 if task is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001331 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001332 if not task.startswith("do_"):
1333 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001335 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001336 fn = self.matchFile(fn, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001337
1338 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001339 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001340
Andrew Geissler5a43b432020-06-13 10:46:56 -05001341 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001342
Patrick Williamse760df82023-05-26 11:10:49 -05001343 layername = self.collections[mc].calc_bbfile_priority(fn)[2]
1344 infos = bb_caches[mc].parse(fn, self.collections[mc].get_file_appends(fn), layername)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001345 infos = dict(infos)
1346
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001347 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001348 try:
1349 info_array = infos[fn]
1350 except KeyError:
1351 bb.fatal("%s does not exist" % fn)
1352
1353 if info_array[0].skipped:
1354 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1355
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001356 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001357
1358 # Tweak some variables
1359 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001360 self.recipecaches[mc].ignored_dependencies = set()
1361 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001362 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001363
1364 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001365 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1366 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001367 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1368 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001369
Andrew Geissler517393d2023-01-13 08:55:19 -06001370 bb.parse.siggen.setup_datacache(self.recipecaches)
1371
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001372 # Invalidate task for target if force mode active
1373 if self.configuration.force:
1374 logger.verbose("Invalidate task %s, %s", task, fn)
Andrew Geissler517393d2023-01-13 08:55:19 -06001375 bb.parse.siggen.invalidate_task(task, fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001376
1377 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001378 taskdata = {}
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001379 taskdata[mc] = bb.taskdata.TaskData(self.configuration.halt)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001380 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001381
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001382 if quietlog:
1383 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1384 bb.runqueue.logger.setLevel(logging.WARNING)
1385
1386 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1387 if fireevents:
1388 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Patrick Williamsda295312023-12-05 16:48:56 -06001389 if self.eventlog:
1390 self.eventlog[2].write_variables()
Andrew Geissler517393d2023-01-13 08:55:19 -06001391 bb.event.enable_heartbeat()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001392
1393 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001394 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001395
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001396 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001397
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001398 def buildFileIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001399
1400 msg = None
1401 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001402 if halt or self.state == state.forceshutdown:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001403 rq.finish_runqueue(True)
1404 msg = "Forced shutdown"
1405 interrupted = 2
1406 elif self.state == state.shutdown:
1407 rq.finish_runqueue(False)
1408 msg = "Stopped build"
1409 interrupted = 1
1410 failures = 0
1411 try:
1412 retval = rq.execute_runqueue()
1413 except runqueue.TaskFailure as exc:
1414 failures += len(exc.args)
1415 retval = False
1416 except SystemExit as exc:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001417 if quietlog:
1418 bb.runqueue.logger.setLevel(rqloglevel)
Andrew Geissler517393d2023-01-13 08:55:19 -06001419 return bb.server.process.idleFinish(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001420
1421 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001422 if fireevents:
1423 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 -06001424 bb.event.disable_heartbeat()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001425 # We trashed self.recipecaches above
Andrew Geissler028142b2023-05-05 11:29:21 -05001426 self._parsecache_set(False)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001427 self.configuration.limited_deps = False
1428 bb.parse.siggen.reset(self.data)
1429 if quietlog:
1430 bb.runqueue.logger.setLevel(rqloglevel)
Andrew Geissler517393d2023-01-13 08:55:19 -06001431 return bb.server.process.idleFinish(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001432 if retval is True:
1433 return True
1434 return retval
1435
Andrew Geissler635e0e42020-08-21 15:58:33 -05001436 self.idleCallBackRegister(buildFileIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001437
Andrew Geissler220dafd2023-10-04 10:18:08 -05001438 def getTaskSignatures(self, target, tasks):
1439 sig = []
1440 getAllTaskSignatures = False
1441
1442 if not tasks:
1443 tasks = ["do_build"]
1444 getAllTaskSignatures = True
1445
1446 for task in tasks:
1447 taskdata, runlist = self.buildTaskData(target, task, self.configuration.halt)
1448 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
1449 rq.rqdata.prepare()
1450
1451 for l in runlist:
1452 mc, pn, taskname, fn = l
1453
1454 taskdep = rq.rqdata.dataCaches[mc].task_deps[fn]
1455 for t in taskdep['tasks']:
1456 if t in taskdep['nostamp'] or "setscene" in t:
1457 continue
1458 tid = bb.runqueue.build_tid(mc, fn, t)
1459
1460 if t in task or getAllTaskSignatures:
1461 try:
1462 rq.rqdata.prepare_task_hash(tid)
1463 sig.append([pn, t, rq.rqdata.get_task_unihash(tid)])
1464 except KeyError:
1465 sig.append(self.getTaskSignatures(target, [t])[0])
1466
1467 return sig
1468
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001469 def buildTargets(self, targets, task):
1470 """
1471 Attempt to build the targets specified
1472 """
1473
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001474 def buildTargetsIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001475 msg = None
1476 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001477 if halt or self.state == state.forceshutdown:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001478 bb.event._should_exit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001479 rq.finish_runqueue(True)
1480 msg = "Forced shutdown"
1481 interrupted = 2
1482 elif self.state == state.shutdown:
1483 rq.finish_runqueue(False)
1484 msg = "Stopped build"
1485 interrupted = 1
1486 failures = 0
1487 try:
1488 retval = rq.execute_runqueue()
1489 except runqueue.TaskFailure as exc:
1490 failures += len(exc.args)
1491 retval = False
1492 except SystemExit as exc:
Andrew Geissler517393d2023-01-13 08:55:19 -06001493 return bb.server.process.idleFinish(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001494
1495 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001496 try:
1497 for mc in self.multiconfigs:
1498 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1499 finally:
Andrew Geissler517393d2023-01-13 08:55:19 -06001500 bb.event.disable_heartbeat()
1501 return bb.server.process.idleFinish(msg)
1502
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001503 if retval is True:
1504 return True
1505 return retval
1506
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001507 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001508 self.buildSetVars()
1509
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001510 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001511 if task is None:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001512 task = self.configuration.cmd
1513
1514 if not task.startswith("do_"):
1515 task = "do_%s" % task
1516
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001517 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1518
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001519 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001520
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001521 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.halt)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001522
1523 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001524
1525 # make targets to always look as <target>:do_<task>
1526 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001527 for target in runlist:
1528 if target[0]:
Brad Bishop15ae2502019-06-18 21:44:24 -04001529 ntargets.append("mc:%s:%s:%s" % (target[0], target[1], target[2]))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001530 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001531
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001532 for mc in self.multiconfigs:
1533 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Patrick Williamsda295312023-12-05 16:48:56 -06001534 if self.eventlog:
1535 self.eventlog[2].write_variables()
Andrew Geissler517393d2023-01-13 08:55:19 -06001536 bb.event.enable_heartbeat()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001537
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001538 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001539 if 'universe' in targets:
1540 rq.rqdata.warn_multi_bb = True
1541
Andrew Geissler635e0e42020-08-21 15:58:33 -05001542 self.idleCallBackRegister(buildTargetsIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001543
1544
1545 def getAllKeysWithFlags(self, flaglist):
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001546 def dummy_autorev(d):
1547 return
1548
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001549 dump = {}
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001550 # Horrible but for now we need to avoid any sideeffects of autorev being called
1551 saved = bb.fetch2.get_autorev
1552 bb.fetch2.get_autorev = dummy_autorev
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001553 for k in self.data.keys():
1554 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001555 expand = True
1556 flags = self.data.getVarFlags(k)
1557 if flags and "func" in flags and "python" in flags:
1558 expand = False
1559 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001560 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1561 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001562 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001563 'history' : self.data.varhistory.variable(k),
1564 }
1565 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001566 if flags and d in flags:
1567 dump[k][d] = flags[d]
1568 else:
1569 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001570 except Exception as e:
1571 print(e)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001572 bb.fetch2.get_autorev = saved
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001573 return dump
1574
1575
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001576 def updateCacheSync(self):
1577 if self.state == state.running:
1578 return
1579
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001580 if not self.baseconfig_valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001581 logger.debug("Reloading base configuration data")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001582 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001583 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001584
1585 # This is called for all async commands when self.state != running
1586 def updateCache(self):
1587 if self.state == state.running:
1588 return
1589
1590 if self.state in (state.shutdown, state.forceshutdown, state.error):
1591 if hasattr(self.parser, 'shutdown'):
Andrew Geissler9aee5002022-03-30 16:27:02 +00001592 self.parser.shutdown(clean=False)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001593 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001594 raise bb.BBHandledException()
1595
1596 if self.state != state.parsing:
1597 self.updateCacheSync()
1598
1599 if self.state != state.parsing and not self.parsecache_valid:
Andrew Geissler8f840682023-07-21 09:09:43 -05001600 bb.server.process.serverlog("Parsing started")
Andrew Geissler220dafd2023-10-04 10:18:08 -05001601 self.parsewatched = {}
Patrick Williamsde0582f2022-04-08 10:23:27 -05001602
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001603 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001604 self.parseConfiguration ()
1605 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001606 for mc in self.multiconfigs:
1607 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001608
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001609 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001610 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001611 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001612
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001613 for dep in self.configuration.extra_assume_provided:
1614 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001615
Andrew Geissler5a43b432020-06-13 10:46:56 -05001616 mcfilelist = {}
1617 total_masked = 0
1618 searchdirs = set()
1619 for mc in self.multiconfigs:
Andrew Geissler5a43b432020-06-13 10:46:56 -05001620 (filelist, masked, search) = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
1621
1622 mcfilelist[mc] = filelist
1623 total_masked += masked
1624 searchdirs |= set(search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001625
Andrew Geissler220dafd2023-10-04 10:18:08 -05001626 # Add mtimes for directories searched for bb/bbappend files
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001627 for dirent in searchdirs:
Andrew Geissler220dafd2023-10-04 10:18:08 -05001628 self.add_filewatch([(dirent, bb.parse.cached_mtime_noerror(dirent))])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001629
Andrew Geissler5a43b432020-06-13 10:46:56 -05001630 self.parser = CookerParser(self, mcfilelist, total_masked)
Andrew Geissler028142b2023-05-05 11:29:21 -05001631 self._parsecache_set(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001632
1633 self.state = state.parsing
1634
1635 if not self.parser.parse_next():
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001636 collectlog.debug("parsing complete")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001637 if self.parser.error:
1638 raise bb.BBHandledException()
1639 self.show_appends_with_no_recipes()
1640 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001641 for mc in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001642 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 -05001643 self.state = state.running
1644
1645 # Send an event listing all stamps reachable after parsing
1646 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001647 for mc in self.multiconfigs:
1648 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1649 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001650 return None
1651
1652 return True
1653
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001654 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001655
1656 # Return a copy, don't modify the original
1657 pkgs_to_build = pkgs_to_build[:]
1658
Andrew Geissler595f6302022-01-24 19:11:47 +00001659 if not pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001660 raise NothingToBuild
1661
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001662 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001663 for pkg in pkgs_to_build.copy():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001664 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001665 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Brad Bishop15ae2502019-06-18 21:44:24 -04001666 if pkg.startswith("multiconfig:"):
1667 pkgs_to_build.remove(pkg)
1668 pkgs_to_build.append(pkg.replace("multiconfig:", "mc:"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001669
1670 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001671 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001672 for mc in self.multiconfigs:
1673 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1674 for t in self.recipecaches[mc].world_target:
1675 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001676 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001677 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001678
1679 if 'universe' in pkgs_to_build:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001680 parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001681 parselog.debug("collating packages for \"universe\"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001682 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001683 for mc in self.multiconfigs:
1684 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001685 if task:
1686 foundtask = False
1687 for provider_fn in self.recipecaches[mc].providers[t]:
1688 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1689 foundtask = True
1690 break
1691 if not foundtask:
1692 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1693 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001694 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001695 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001696 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001697
1698 return pkgs_to_build
1699
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001700 def pre_serve(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001701 return
1702
1703 def post_serve(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001704 self.shutdown(force=True)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001705 prserv.serv.auto_shutdown()
Patrick Williams45852732022-04-02 08:58:32 -05001706 if hasattr(bb.parse, "siggen"):
1707 bb.parse.siggen.exit()
Brad Bishop08902b02019-08-20 09:16:51 -04001708 if self.hashserv:
1709 self.hashserv.process.terminate()
1710 self.hashserv.process.join()
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001711 if hasattr(self, "data"):
1712 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001713
Andrew Geissler517393d2023-01-13 08:55:19 -06001714 def shutdown(self, force=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001715 if force:
1716 self.state = state.forceshutdown
Patrick Williams8e7b46e2023-05-01 14:19:06 -05001717 bb.event._should_exit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001718 else:
1719 self.state = state.shutdown
1720
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001721 if self.parser:
Andrew Geissler517393d2023-01-13 08:55:19 -06001722 self.parser.shutdown(clean=False)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001723 self.parser.final_cleanup()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001724
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001725 def finishcommand(self):
Andrew Geissler517393d2023-01-13 08:55:19 -06001726 if hasattr(self.parser, 'shutdown'):
1727 self.parser.shutdown(clean=False)
1728 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001729 self.state = state.initial
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001730 bb.event._should_exit.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001731
1732 def reset(self):
Patrick Williams45852732022-04-02 08:58:32 -05001733 if hasattr(bb.parse, "siggen"):
1734 bb.parse.siggen.exit()
Andrew Geissler517393d2023-01-13 08:55:19 -06001735 self.finishcommand()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001736 self.initConfigurationData()
Brad Bishop08902b02019-08-20 09:16:51 -04001737 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001738
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001739 def clientComplete(self):
1740 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001741 self.finishcommand()
1742 self.extraconfigdata = {}
1743 self.command.reset()
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001744 if hasattr(self, "data"):
1745 self.databuilder.reset()
1746 self.data = self.databuilder.data
Andrew Geissler517393d2023-01-13 08:55:19 -06001747 # In theory tinfoil could have modified the base data before parsing,
1748 # ideally need to track if anything did modify the datastore
Andrew Geissler028142b2023-05-05 11:29:21 -05001749 self._parsecache_set(False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001750
1751class CookerExit(bb.event.Event):
1752 """
1753 Notify clients of the Cooker shutdown
1754 """
1755
1756 def __init__(self):
1757 bb.event.Event.__init__(self)
1758
1759
1760class CookerCollectFiles(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001761 def __init__(self, priorities, mc=''):
1762 self.mc = mc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001763 self.bbappends = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001764 # Priorities is a list of tuples, with the second element as the pattern.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001765 # We need to sort the list with the longest pattern first, and so on to
1766 # the shortest. This allows nested layers to be properly evaluated.
1767 self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001768
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001769 def calc_bbfile_priority(self, filename):
Patrick Williamse760df82023-05-26 11:10:49 -05001770 for layername, _, regex, pri in self.bbfile_config_priorities:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001771 if regex.match(filename):
Patrick Williamse760df82023-05-26 11:10:49 -05001772 return pri, regex, layername
1773 return 0, None, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001774
1775 def get_bbfiles(self):
1776 """Get list of default .bb files by reading out the current directory"""
1777 path = os.getcwd()
1778 contents = os.listdir(path)
1779 bbfiles = []
1780 for f in contents:
1781 if f.endswith(".bb"):
1782 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1783 return bbfiles
1784
1785 def find_bbfiles(self, path):
1786 """Find all the .bb and .bbappend files in a directory"""
1787 found = []
1788 for dir, dirs, files in os.walk(path):
1789 for ignored in ('SCCS', 'CVS', '.svn'):
1790 if ignored in dirs:
1791 dirs.remove(ignored)
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001792 found += [os.path.join(dir, f) for f in files if (f.endswith(('.bb', '.bbappend')))]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001793
1794 return found
1795
1796 def collect_bbfiles(self, config, eventdata):
1797 """Collect all available .bb build files"""
1798 masked = 0
1799
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001800 collectlog.debug("collecting .bb files")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001801
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001802 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001803
1804 # Sort files by priority
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001805 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem)[0] )
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001806 config.setVar("BBFILES_PRIORITIZED", " ".join(files))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001807
Andrew Geissler595f6302022-01-24 19:11:47 +00001808 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001809 files = self.get_bbfiles()
1810
Andrew Geissler595f6302022-01-24 19:11:47 +00001811 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001812 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1813 bb.event.fire(CookerExit(), eventdata)
1814
Andrew Geissler220dafd2023-10-04 10:18:08 -05001815 # 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 -05001816 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001817 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001818 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001819 if hasattr(os, 'scandir'):
1820 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001821 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001822
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001823 def ourlistdir(d):
1824 searchdirs.append(d)
1825 return origlistdir(d)
1826
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001827 def ourscandir(d):
1828 searchdirs.append(d)
1829 return origscandir(d)
1830
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001831 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001832 if hasattr(os, 'scandir'):
1833 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001834 try:
1835 # Can't use set here as order is important
1836 newfiles = []
1837 for f in files:
1838 if os.path.isdir(f):
1839 dirfiles = self.find_bbfiles(f)
1840 for g in dirfiles:
1841 if g not in newfiles:
1842 newfiles.append(g)
1843 else:
1844 globbed = glob.glob(f)
1845 if not globbed and os.path.exists(f):
1846 globbed = [f]
1847 # glob gives files in order on disk. Sort to be deterministic.
1848 for g in sorted(globbed):
1849 if g not in newfiles:
1850 newfiles.append(g)
1851 finally:
1852 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001853 if hasattr(os, 'scandir'):
1854 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001855
1856 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001857
1858 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001859 # First validate the individual regular expressions and ignore any
1860 # that do not compile
1861 bbmasks = []
1862 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001863 # When constructing an older style single regex, it's possible for BBMASK
1864 # to end up beginning with '|', which matches and masks _everything_.
1865 if mask.startswith("|"):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001866 collectlog.warning("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001867 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001868 try:
1869 re.compile(mask)
1870 bbmasks.append(mask)
Andrew Geissler78b72792022-06-14 06:47:25 -05001871 except re.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001872 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1873
1874 # Then validate the combined regular expressions. This should never
1875 # fail, but better safe than sorry...
1876 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001877 try:
1878 bbmask_compiled = re.compile(bbmask)
Andrew Geissler78b72792022-06-14 06:47:25 -05001879 except re.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001880 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1881 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001882
1883 bbfiles = []
1884 bbappend = []
1885 for f in newfiles:
1886 if bbmask and bbmask_compiled.search(f):
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001887 collectlog.debug("skipping masked file %s", f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001888 masked += 1
1889 continue
1890 if f.endswith('.bb'):
1891 bbfiles.append(f)
1892 elif f.endswith('.bbappend'):
1893 bbappend.append(f)
1894 else:
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001895 collectlog.debug("skipping %s: unknown file extension", f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001896
1897 # Build a list of .bbappend files for each .bb file
1898 for f in bbappend:
1899 base = os.path.basename(f).replace('.bbappend', '.bb')
1900 self.bbappends.append((base, f))
1901
1902 # Find overlayed recipes
1903 # bbfiles will be in priority order which makes this easy
1904 bbfile_seen = dict()
1905 self.overlayed = defaultdict(list)
1906 for f in reversed(bbfiles):
1907 base = os.path.basename(f)
1908 if base not in bbfile_seen:
1909 bbfile_seen[base] = f
1910 else:
1911 topfile = bbfile_seen[base]
1912 self.overlayed[topfile].append(f)
1913
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001914 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001915
1916 def get_file_appends(self, fn):
1917 """
1918 Returns a list of .bbappend files to apply to fn
1919 """
1920 filelist = []
1921 f = os.path.basename(fn)
1922 for b in self.bbappends:
1923 (bbappend, filename) = b
1924 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1925 filelist.append(filename)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001926 return tuple(filelist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001927
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001928 def collection_priorities(self, pkgfns, fns, d):
1929 # Return the priorities of the entries in pkgfns
1930 # Also check that all the regexes in self.bbfile_config_priorities are used
1931 # (but to do that we need to ensure skipped recipes aren't counted, nor
1932 # collections in BBFILE_PATTERN_IGNORE_EMPTY)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001933
1934 priorities = {}
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001935 seen = set()
1936 matched = set()
1937
1938 matched_regex = set()
1939 unmatched_regex = set()
1940 for _, _, regex, _ in self.bbfile_config_priorities:
1941 unmatched_regex.add(regex)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001942
1943 # Calculate priorities for each file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001944 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001945 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Patrick Williamse760df82023-05-26 11:10:49 -05001946 priorities[p], regex, _ = self.calc_bbfile_priority(realfn)
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001947 if regex in unmatched_regex:
1948 matched_regex.add(regex)
1949 unmatched_regex.remove(regex)
1950 seen.add(realfn)
1951 if regex:
1952 matched.add(realfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001953
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001954 if unmatched_regex:
1955 # Account for bbappend files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001956 for b in self.bbappends:
1957 (bbfile, append) = b
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001958 seen.add(append)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001959
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001960 # Account for skipped recipes
1961 seen.update(fns)
1962
1963 seen.difference_update(matched)
1964
1965 def already_matched(fn):
1966 for regex in matched_regex:
1967 if regex.match(fn):
1968 return True
1969 return False
1970
1971 for unmatch in unmatched_regex.copy():
1972 for fn in seen:
1973 if unmatch.match(fn):
1974 # If the bbappend or file was already matched by another regex, skip it
1975 # e.g. for a layer within a layer, the outer regex could match, the inner
1976 # regex may match nothing and we should warn about that
1977 if already_matched(fn):
1978 continue
1979 unmatched_regex.remove(unmatch)
1980 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001981
1982 for collection, pattern, regex, _ in self.bbfile_config_priorities:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001983 if regex in unmatched_regex:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001984 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Andrew Geissler5a43b432020-06-13 10:46:56 -05001985 collectlog.warning("No bb files in %s matched BBFILE_PATTERN_%s '%s'" % (self.mc if self.mc else 'default',
1986 collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001987
1988 return priorities
1989
1990class ParsingFailure(Exception):
1991 def __init__(self, realexception, recipe):
1992 self.realexception = realexception
1993 self.recipe = recipe
1994 Exception.__init__(self, realexception, recipe)
1995
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001996class Parser(multiprocessing.Process):
Andrew Geissler9aee5002022-03-30 16:27:02 +00001997 def __init__(self, jobs, results, quit, profile):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001998 self.jobs = jobs
1999 self.results = results
2000 self.quit = quit
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002001 multiprocessing.Process.__init__(self)
2002 self.context = bb.utils.get_context().copy()
2003 self.handlers = bb.event.get_class_handlers().copy()
2004 self.profile = profile
Andrew Geissler9aee5002022-03-30 16:27:02 +00002005 self.queue_signals = False
2006 self.signal_received = []
2007 self.signal_threadlock = threading.Lock()
2008
2009 def catch_sig(self, signum, frame):
2010 if self.queue_signals:
2011 self.signal_received.append(signum)
2012 else:
2013 self.handle_sig(signum, frame)
2014
2015 def handle_sig(self, signum, frame):
2016 if signum == signal.SIGTERM:
2017 signal.signal(signal.SIGTERM, signal.SIG_DFL)
2018 os.kill(os.getpid(), signal.SIGTERM)
2019 elif signum == signal.SIGINT:
2020 signal.default_int_handler(signum, frame)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002021
2022 def run(self):
2023
2024 if not self.profile:
2025 self.realrun()
2026 return
2027
2028 try:
2029 import cProfile as profile
2030 except:
2031 import profile
2032 prof = profile.Profile()
2033 try:
2034 profile.Profile.runcall(prof, self.realrun)
2035 finally:
2036 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
2037 prof.dump_stats(logfile)
2038
2039 def realrun(self):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002040 # Signal handling here is hard. We must not terminate any process or thread holding the write
2041 # lock for the event stream as it will not be released, ever, and things will hang.
2042 # Python handles signals in the main thread/process but they can be raised from any thread and
2043 # we want to defer processing of any SIGTERM/SIGINT signal until we're outside the critical section
2044 # and don't hold the lock (see server/process.py). We therefore always catch the signals (so any
2045 # new thread should also do so) and we defer handling but we handle with the local thread lock
2046 # held (a threading lock, not a multiprocessing one) so that no other thread in the process
2047 # can be in the critical section.
2048 signal.signal(signal.SIGTERM, self.catch_sig)
2049 signal.signal(signal.SIGHUP, signal.SIG_DFL)
2050 signal.signal(signal.SIGINT, self.catch_sig)
2051 bb.utils.set_process_name(multiprocessing.current_process().name)
2052 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2053 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002054
2055 pending = []
Andrew Geissler517393d2023-01-13 08:55:19 -06002056 havejobs = True
Andrew Geissler9aee5002022-03-30 16:27:02 +00002057 try:
Andrew Geissler517393d2023-01-13 08:55:19 -06002058 while havejobs or pending:
2059 if self.quit.is_set():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002060 break
Andrew Geissler9aee5002022-03-30 16:27:02 +00002061
Andrew Geissler517393d2023-01-13 08:55:19 -06002062 job = None
2063 try:
2064 job = self.jobs.pop()
2065 except IndexError:
2066 havejobs = False
2067 if job:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002068 result = self.parse(*job)
2069 # Clear the siggen cache after parsing to control memory usage, its huge
2070 bb.parse.siggen.postparsing_clean_cache()
Andrew Geissler9aee5002022-03-30 16:27:02 +00002071 pending.append(result)
Andrew Geissler517393d2023-01-13 08:55:19 -06002072
2073 if pending:
2074 try:
2075 result = pending.pop()
2076 self.results.put(result, timeout=0.05)
2077 except queue.Full:
2078 pending.append(result)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002079 finally:
2080 self.results.close()
2081 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002082
Patrick Williamse760df82023-05-26 11:10:49 -05002083 def parse(self, mc, cache, filename, appends, layername):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002084 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002085 origfilter = bb.event.LogHandler.filter
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002086 # Record the filename we're parsing into any events generated
2087 def parse_filter(self, record):
2088 record.taskpid = bb.event.worker_pid
2089 record.fn = filename
2090 return True
2091
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002092 # Reset our environment and handlers to the original settings
2093 bb.utils.set_context(self.context.copy())
2094 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002095 bb.event.LogHandler.filter = parse_filter
2096
Patrick Williamse760df82023-05-26 11:10:49 -05002097 return True, mc, cache.parse(filename, appends, layername)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002098 except Exception as exc:
2099 tb = sys.exc_info()[2]
2100 exc.recipe = filename
2101 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002102 return True, None, exc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002103 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
2104 # and for example a worker thread doesn't just exit on its own in response to
2105 # a SystemExit event for example.
2106 except BaseException as exc:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002107 return True, None, ParsingFailure(exc, filename)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002108 finally:
2109 bb.event.LogHandler.filter = origfilter
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002110
2111class CookerParser(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002112 def __init__(self, cooker, mcfilelist, masked):
2113 self.mcfilelist = mcfilelist
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002114 self.cooker = cooker
2115 self.cfgdata = cooker.data
2116 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002117 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002118
2119 # Accounting statistics
2120 self.parsed = 0
2121 self.cached = 0
2122 self.error = 0
2123 self.masked = masked
2124
2125 self.skipped = 0
2126 self.virtuals = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002127
2128 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002129 self.process_names = []
2130
Andrew Geissler5a43b432020-06-13 10:46:56 -05002131 self.bb_caches = bb.cache.MulticonfigCache(self.cfgbuilder, self.cfghash, cooker.caches_array)
2132 self.fromcache = set()
2133 self.willparse = set()
2134 for mc in self.cooker.multiconfigs:
2135 for filename in self.mcfilelist[mc]:
2136 appends = self.cooker.collections[mc].get_file_appends(filename)
Patrick Williamse760df82023-05-26 11:10:49 -05002137 layername = self.cooker.collections[mc].calc_bbfile_priority(filename)[2]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002138 if not self.bb_caches[mc].cacheValid(filename, appends):
Patrick Williamse760df82023-05-26 11:10:49 -05002139 self.willparse.add((mc, self.bb_caches[mc], filename, appends, layername))
Andrew Geissler5a43b432020-06-13 10:46:56 -05002140 else:
Patrick Williamse760df82023-05-26 11:10:49 -05002141 self.fromcache.add((mc, self.bb_caches[mc], filename, appends, layername))
Andrew Geissler5a43b432020-06-13 10:46:56 -05002142
2143 self.total = len(self.fromcache) + len(self.willparse)
2144 self.toparse = len(self.willparse)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002145 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002146
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002147 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Andrew Geissler5a43b432020-06-13 10:46:56 -05002148 multiprocessing.cpu_count()), self.toparse)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002149
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002150 bb.cache.SiggenRecipeInfo.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002151 self.start()
2152 self.haveshutdown = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002153 self.syncthread = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002154
2155 def start(self):
2156 self.results = self.load_cached()
2157 self.processes = []
2158 if self.toparse:
2159 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002160
Andrew Geissler517393d2023-01-13 08:55:19 -06002161 self.parser_quit = multiprocessing.Event()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002162 self.result_queue = multiprocessing.Queue()
Brad Bishop19323692019-04-05 15:28:33 -04002163
2164 def chunkify(lst,n):
2165 return [lst[i::n] for i in range(n)]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002166 self.jobs = chunkify(list(self.willparse), self.num_processes)
Brad Bishop19323692019-04-05 15:28:33 -04002167
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002168 for i in range(0, self.num_processes):
Andrew Geissler9aee5002022-03-30 16:27:02 +00002169 parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, self.cooker.configuration.profile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002170 parser.start()
2171 self.process_names.append(parser.name)
2172 self.processes.append(parser)
2173
2174 self.results = itertools.chain(self.results, self.parse_generator())
2175
Patrick Williams8e7b46e2023-05-01 14:19:06 -05002176 def shutdown(self, clean=True, eventmsg="Parsing halted due to errors"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002177 if not self.toparse:
2178 return
2179 if self.haveshutdown:
2180 return
2181 self.haveshutdown = True
2182
2183 if clean:
2184 event = bb.event.ParseCompleted(self.cached, self.parsed,
2185 self.skipped, self.masked,
2186 self.virtuals, self.error,
2187 self.total)
2188
2189 bb.event.fire(event, self.cfgdata)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002190 else:
Patrick Williams8e7b46e2023-05-01 14:19:06 -05002191 bb.event.fire(bb.event.ParseError(eventmsg), self.cfgdata)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002192 bb.error("Parsing halted due to errors, see error messages above")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002193
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002194 # Cleanup the queue before call process.join(), otherwise there might be
2195 # deadlocks.
2196 while True:
2197 try:
2198 self.result_queue.get(timeout=0.25)
2199 except queue.Empty:
2200 break
2201
Andrew Geissler517393d2023-01-13 08:55:19 -06002202 def sync_caches():
2203 for c in self.bb_caches.values():
2204 bb.cache.SiggenRecipeInfo.reset()
2205 c.sync()
2206
2207 self.syncthread = threading.Thread(target=sync_caches, name="SyncThread")
2208 self.syncthread.start()
2209
2210 self.parser_quit.set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002211
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002212 for process in self.processes:
Andrew Geissler9aee5002022-03-30 16:27:02 +00002213 process.join(0.5)
2214
2215 for process in self.processes:
2216 if process.exitcode is None:
2217 os.kill(process.pid, signal.SIGINT)
2218
2219 for process in self.processes:
2220 process.join(0.5)
2221
2222 for process in self.processes:
2223 if process.exitcode is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002224 process.terminate()
Andrew Geissler9aee5002022-03-30 16:27:02 +00002225
2226 for process in self.processes:
2227 process.join()
2228 # Added in 3.7, cleans up zombies
2229 if hasattr(process, "close"):
2230 process.close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002231
Andrew Geisslerc5535c92023-01-27 16:10:19 -06002232 bb.codeparser.parser_cache_save()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002233 bb.codeparser.parser_cache_savemerge()
Andrew Geissler517393d2023-01-13 08:55:19 -06002234 bb.cache.SiggenRecipeInfo.reset()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002235 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002236 if self.cooker.configuration.profile:
2237 profiles = []
2238 for i in self.process_names:
2239 logfile = "profile-parse-%s.log" % i
2240 if os.path.exists(logfile):
2241 profiles.append(logfile)
2242
2243 pout = "profile-parse.log.processed"
2244 bb.utils.process_profilelog(profiles, pout = pout)
2245 print("Processed parsing statistics saved to %s" % (pout))
2246
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002247 def final_cleanup(self):
2248 if self.syncthread:
2249 self.syncthread.join()
2250
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002251 def load_cached(self):
Patrick Williamse760df82023-05-26 11:10:49 -05002252 for mc, cache, filename, appends, layername in self.fromcache:
Andrew Geissler517393d2023-01-13 08:55:19 -06002253 infos = cache.loadCached(filename, appends)
2254 yield False, mc, infos
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002255
2256 def parse_generator(self):
Andrew Geissler595f6302022-01-24 19:11:47 +00002257 empty = False
2258 while self.processes or not empty:
2259 for process in self.processes.copy():
2260 if not process.is_alive():
2261 process.join()
2262 self.processes.remove(process)
2263
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002264 if self.parsed >= self.toparse:
2265 break
2266
2267 try:
2268 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002269 except queue.Empty:
Andrew Geissler595f6302022-01-24 19:11:47 +00002270 empty = True
Andrew Geissler9aee5002022-03-30 16:27:02 +00002271 yield None, None, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002272 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002273 empty = False
Andrew Geissler9aee5002022-03-30 16:27:02 +00002274 yield result
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002275
Andrew Geissler595f6302022-01-24 19:11:47 +00002276 if not (self.parsed >= self.toparse):
2277 raise bb.parse.ParseError("Not all recipes parsed, parser thread killed/died? Exiting.", None)
2278
2279
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002280 def parse_next(self):
2281 result = []
2282 parsed = None
2283 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002284 parsed, mc, result = next(self.results)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002285 if isinstance(result, BaseException):
2286 # Turn exceptions back into exceptions
2287 raise result
2288 if parsed is None:
2289 # Timeout, loop back through the main loop
2290 return True
2291
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002292 except StopIteration:
2293 self.shutdown()
2294 return False
2295 except bb.BBHandledException as exc:
2296 self.error += 1
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002297 logger.debug('Failed to parse recipe: %s' % exc.recipe)
Andrew Geissler9aee5002022-03-30 16:27:02 +00002298 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002299 return False
2300 except ParsingFailure as exc:
2301 self.error += 1
2302 logger.error('Unable to parse %s: %s' %
2303 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002304 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002305 return False
2306 except bb.parse.ParseError as exc:
2307 self.error += 1
2308 logger.error(str(exc))
Patrick Williams8e7b46e2023-05-01 14:19:06 -05002309 self.shutdown(clean=False, eventmsg=str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002310 return False
2311 except bb.data_smart.ExpansionError as exc:
2312 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002313 bbdir = os.path.dirname(__file__) + os.sep
2314 etype, value, _ = sys.exc_info()
2315 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2316 logger.error('ExpansionError during parsing %s', value.recipe,
2317 exc_info=(etype, value, tb))
Andrew Geissler9aee5002022-03-30 16:27:02 +00002318 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002319 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002320 except Exception as exc:
2321 self.error += 1
2322 etype, value, tb = sys.exc_info()
2323 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002324 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002325 exc_info=(etype, value, exc.traceback))
2326 else:
2327 # Most likely, an exception occurred during raising an exception
2328 import traceback
2329 logger.error('Exception during parse: %s' % traceback.format_exc())
Andrew Geissler9aee5002022-03-30 16:27:02 +00002330 self.shutdown(clean=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002331 return False
2332
2333 self.current += 1
2334 self.virtuals += len(result)
2335 if parsed:
2336 self.parsed += 1
2337 if self.parsed % self.progress_chunk == 0:
2338 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2339 self.cfgdata)
2340 else:
2341 self.cached += 1
2342
2343 for virtualfn, info_array in result:
2344 if info_array[0].skipped:
2345 self.skipped += 1
2346 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Andrew Geissler5a43b432020-06-13 10:46:56 -05002347 self.bb_caches[mc].add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002348 parsed=parsed, watcher = self.cooker.add_filewatch)
2349 return True
2350
2351 def reparse(self, filename):
Andrew Geissler517393d2023-01-13 08:55:19 -06002352 bb.cache.SiggenRecipeInfo.reset()
Andrew Geissler5a43b432020-06-13 10:46:56 -05002353 to_reparse = set()
2354 for mc in self.cooker.multiconfigs:
Patrick Williamse760df82023-05-26 11:10:49 -05002355 layername = self.cooker.collections[mc].calc_bbfile_priority(filename)[2]
2356 to_reparse.add((mc, filename, self.cooker.collections[mc].get_file_appends(filename), layername))
Andrew Geissler5a43b432020-06-13 10:46:56 -05002357
Patrick Williamse760df82023-05-26 11:10:49 -05002358 for mc, filename, appends, layername in to_reparse:
2359 infos = self.bb_caches[mc].parse(filename, appends, layername)
Andrew Geissler5a43b432020-06-13 10:46:56 -05002360 for vfn, info_array in infos:
2361 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)