blob: 0133a3278ffa9e06888ac3f12ab375c6eaa2780a [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#
2# Copyright (C) 2003, 2004 Chris Larson
3# Copyright (C) 2003, 2004 Phil Blundell
4# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
5# Copyright (C) 2005 Holger Hans Peter Freyther
6# Copyright (C) 2005 ROAD GmbH
7# Copyright (C) 2006 - 2007 Richard Purdie
8#
Brad Bishopc342db32019-05-15 21:57:59 -04009# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010#
Patrick Williamsc0f7c042017-02-23 20:41:17 -060011
Patrick Williamsc124f4f2015-09-15 14:41:29 -050012import sys, os, glob, os.path, re, time
Patrick Williamsc124f4f2015-09-15 14:41:29 -050013import itertools
14import logging
15import multiprocessing
16import sre_constants
17import threading
Patrick Williamsc0f7c042017-02-23 20:41:17 -060018from io import StringIO, UnsupportedOperation
Patrick Williamsc124f4f2015-09-15 14:41:29 -050019from contextlib import closing
Patrick Williamsc0f7c042017-02-23 20:41:17 -060020from collections import defaultdict, namedtuple
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021import bb, bb.exceptions, bb.command
22from bb import utils, data, parse, event, cache, providers, taskdata, runqueue, build
Patrick Williamsc0f7c042017-02-23 20:41:17 -060023import queue
Patrick Williamsc124f4f2015-09-15 14:41:29 -050024import signal
Patrick Williamsc124f4f2015-09-15 14:41:29 -050025import prserv.serv
26import pyinotify
Patrick Williamsc0f7c042017-02-23 20:41:17 -060027import json
28import pickle
29import codecs
Brad Bishop08902b02019-08-20 09:16:51 -040030import hashserv
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031
32logger = logging.getLogger("BitBake")
33collectlog = logging.getLogger("BitBake.Collection")
34buildlog = logging.getLogger("BitBake.Build")
35parselog = logging.getLogger("BitBake.Parsing")
36providerlog = logging.getLogger("BitBake.Provider")
37
38class NoSpecificMatch(bb.BBHandledException):
39 """
40 Exception raised when no or multiple file matches are found
41 """
42
43class NothingToBuild(Exception):
44 """
45 Exception raised when there is nothing to build
46 """
47
48class CollectionError(bb.BBHandledException):
49 """
50 Exception raised when layer configuration is incorrect
51 """
52
53class state:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060054 initial, parsing, running, shutdown, forceshutdown, stopped, error = list(range(7))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050055
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050056 @classmethod
57 def get_name(cls, code):
58 for name in dir(cls):
59 value = getattr(cls, name)
60 if type(value) == type(cls.initial) and value == code:
61 return name
62 raise ValueError("Invalid status code: %s" % code)
63
Patrick Williamsc124f4f2015-09-15 14:41:29 -050064
65class SkippedPackage:
66 def __init__(self, info = None, reason = None):
67 self.pn = None
68 self.skipreason = None
69 self.provides = None
70 self.rprovides = None
71
72 if info:
73 self.pn = info.pn
74 self.skipreason = info.skipreason
75 self.provides = info.provides
Andrew Geisslerd1e89492021-02-12 15:35:20 -060076 self.rprovides = info.packages + info.rprovides
77 for package in info.packages:
78 self.rprovides += info.rprovides_pkg[package]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050079 elif reason:
80 self.skipreason = reason
81
82
83class CookerFeatures(object):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060084 _feature_list = [HOB_EXTRA_CACHES, BASEDATASTORE_TRACKING, SEND_SANITYEVENTS] = list(range(3))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050085
86 def __init__(self):
87 self._features=set()
88
89 def setFeature(self, f):
90 # validate we got a request for a feature we support
91 if f not in CookerFeatures._feature_list:
92 return
93 self._features.add(f)
94
95 def __contains__(self, f):
96 return f in self._features
97
98 def __iter__(self):
99 return self._features.__iter__()
100
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600101 def __next__(self):
102 return next(self._features)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500103
104
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600105class EventWriter:
106 def __init__(self, cooker, eventfile):
107 self.file_inited = None
108 self.cooker = cooker
109 self.eventfile = eventfile
110 self.event_queue = []
111
112 def write_event(self, event):
113 with open(self.eventfile, "a") as f:
114 try:
115 str_event = codecs.encode(pickle.dumps(event), 'base64').decode('utf-8')
116 f.write("%s\n" % json.dumps({"class": event.__module__ + "." + event.__class__.__name__,
117 "vars": str_event}))
118 except Exception as err:
119 import traceback
120 print(err, traceback.format_exc())
121
122 def send(self, event):
123 if self.file_inited:
124 # we have the file, just write the event
125 self.write_event(event)
126 else:
127 # init on bb.event.BuildStarted
128 name = "%s.%s" % (event.__module__, event.__class__.__name__)
129 if name in ("bb.event.BuildStarted", "bb.cooker.CookerExit"):
130 with open(self.eventfile, "w") as f:
131 f.write("%s\n" % json.dumps({ "allvariables" : self.cooker.getAllKeysWithFlags(["doc", "func"])}))
132
133 self.file_inited = True
134
135 # write pending events
136 for evt in self.event_queue:
137 self.write_event(evt)
138
139 # also write the current event
140 self.write_event(event)
141 else:
142 # queue all events until the file is inited
143 self.event_queue.append(event)
144
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500145#============================================================================#
146# BBCooker
147#============================================================================#
148class BBCooker:
149 """
150 Manages one bitbake build run
151 """
152
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500153 def __init__(self, featureSet=None, idleCallBackRegister=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600154 self.recipecaches = None
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500155 self.eventlog = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500156 self.skiplist = {}
157 self.featureset = CookerFeatures()
158 if featureSet:
159 for f in featureSet:
160 self.featureset.setFeature(f)
161
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500162 self.configuration = bb.cookerdata.CookerConfiguration()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500163
Andrew Geissler635e0e42020-08-21 15:58:33 -0500164 self.idleCallBackRegister = idleCallBackRegister
165
Brad Bishopf058f492019-01-28 23:50:33 -0500166 bb.debug(1, "BBCooker starting %s" % time.time())
167 sys.stdout.flush()
168
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500169 self.configwatcher = pyinotify.WatchManager()
Brad Bishopf058f492019-01-28 23:50:33 -0500170 bb.debug(1, "BBCooker pyinotify1 %s" % time.time())
171 sys.stdout.flush()
172
Andrew Geissler82c905d2020-04-13 13:39:40 -0500173 self.configwatcher.bbseen = set()
174 self.configwatcher.bbwatchedfiles = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500175 self.confignotifier = pyinotify.Notifier(self.configwatcher, self.config_notifications)
Brad Bishopf058f492019-01-28 23:50:33 -0500176 bb.debug(1, "BBCooker pyinotify2 %s" % time.time())
177 sys.stdout.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500178 self.watchmask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CREATE | pyinotify.IN_DELETE | \
179 pyinotify.IN_DELETE_SELF | pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF | \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500180 pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500181 self.watcher = pyinotify.WatchManager()
Brad Bishopf058f492019-01-28 23:50:33 -0500182 bb.debug(1, "BBCooker pyinotify3 %s" % time.time())
183 sys.stdout.flush()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500184 self.watcher.bbseen = set()
185 self.watcher.bbwatchedfiles = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500186 self.notifier = pyinotify.Notifier(self.watcher, self.notifications)
187
Brad Bishopf058f492019-01-28 23:50:33 -0500188 bb.debug(1, "BBCooker pyinotify complete %s" % time.time())
189 sys.stdout.flush()
190
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500191 # If being called by something like tinfoil, we need to clean cached data
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500192 # which may now be invalid
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500193 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500194 bb.parse.BBHandler.cached_statements = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500196 self.ui_cmdline = None
Brad Bishop08902b02019-08-20 09:16:51 -0400197 self.hashserv = None
Brad Bishopa34c0302019-09-23 22:34:48 -0400198 self.hashservaddr = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500199
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500200 self.inotify_modified_files = []
201
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000202 def _process_inotify_updates(server, cooker, halt):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500203 cooker.process_inotify_updates()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500204 return 1.0
205
Andrew Geissler635e0e42020-08-21 15:58:33 -0500206 self.idleCallBackRegister(_process_inotify_updates, self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500207
208 # TOSTOP must not be set or our children will hang when they output
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600209 try:
210 fd = sys.stdout.fileno()
211 if os.isatty(fd):
212 import termios
213 tcattr = termios.tcgetattr(fd)
214 if tcattr[3] & termios.TOSTOP:
215 buildlog.info("The terminal had the TOSTOP bit set, clearing...")
216 tcattr[3] = tcattr[3] & ~termios.TOSTOP
217 termios.tcsetattr(fd, termios.TCSANOW, tcattr)
218 except UnsupportedOperation:
219 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500220
221 self.command = bb.command.Command(self)
222 self.state = state.initial
223
224 self.parser = None
225
226 signal.signal(signal.SIGTERM, self.sigterm_exception)
227 # Let SIGHUP exit as SIGTERM
228 signal.signal(signal.SIGHUP, self.sigterm_exception)
229
Brad Bishopf058f492019-01-28 23:50:33 -0500230 bb.debug(1, "BBCooker startup complete %s" % time.time())
231 sys.stdout.flush()
232
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500233 def init_configdata(self):
234 if not hasattr(self, "data"):
235 self.initConfigurationData()
236 bb.debug(1, "BBCooker parsed base configuration %s" % time.time())
237 sys.stdout.flush()
238 self.handlePRServ()
239
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500240 def process_inotify_updates(self):
241 for n in [self.confignotifier, self.notifier]:
242 if n.check_events(timeout=0):
243 # read notified events and enqeue them
244 n.read_events()
245 n.process_events()
246
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500247 def config_notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500248 if event.maskname == "IN_Q_OVERFLOW":
249 bb.warn("inotify event queue overflowed, invalidating caches.")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500250 self.parsecache_valid = False
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500251 self.baseconfig_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500252 bb.parse.clear_cache()
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500253 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500254 if not event.pathname in self.configwatcher.bbwatchedfiles:
255 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500256 if not event.pathname in self.inotify_modified_files:
257 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500258 self.baseconfig_valid = False
259
260 def notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500261 if event.maskname == "IN_Q_OVERFLOW":
262 bb.warn("inotify event queue overflowed, invalidating caches.")
263 self.parsecache_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500264 bb.parse.clear_cache()
265 return
266 if event.pathname.endswith("bitbake-cookerdaemon.log") \
267 or event.pathname.endswith("bitbake.lock"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500268 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500269 if not event.pathname in self.inotify_modified_files:
270 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271 self.parsecache_valid = False
272
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500273 def add_filewatch(self, deps, watcher=None, dirs=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274 if not watcher:
275 watcher = self.watcher
276 for i in deps:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500277 watcher.bbwatchedfiles.add(i[0])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500278 if dirs:
279 f = i[0]
280 else:
281 f = os.path.dirname(i[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500282 if f in watcher.bbseen:
283 continue
Andrew Geissler82c905d2020-04-13 13:39:40 -0500284 watcher.bbseen.add(f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500285 watchtarget = None
286 while True:
287 # We try and add watches for files that don't exist but if they did, would influence
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500288 # the parser. The parent directory of these files may not exist, in which case we need
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500289 # to watch any parent that does exist for changes.
290 try:
291 watcher.add_watch(f, self.watchmask, quiet=False)
292 if watchtarget:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500293 watcher.bbwatchedfiles.add(watchtarget)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500294 break
295 except pyinotify.WatchManagerError as e:
296 if 'ENOENT' in str(e):
297 watchtarget = f
298 f = os.path.dirname(f)
299 if f in watcher.bbseen:
300 break
Andrew Geissler82c905d2020-04-13 13:39:40 -0500301 watcher.bbseen.add(f)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500302 continue
303 if 'ENOSPC' in str(e):
304 providerlog.error("No space left on device or exceeds fs.inotify.max_user_watches?")
305 providerlog.error("To check max_user_watches: sysctl -n fs.inotify.max_user_watches.")
306 providerlog.error("To modify max_user_watches: sysctl -n -w fs.inotify.max_user_watches=<value>.")
307 providerlog.error("Root privilege is required to modify max_user_watches.")
308 raise
309
310 def sigterm_exception(self, signum, stackframe):
311 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500312 bb.warn("Cooker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500313 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500314 bb.warn("Cooker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500315 self.state = state.forceshutdown
316
317 def setFeatures(self, features):
318 # we only accept a new feature set if we're in state initial, so we can reset without problems
319 if not self.state in [state.initial, state.shutdown, state.forceshutdown, state.stopped, state.error]:
320 raise Exception("Illegal state for feature set change")
321 original_featureset = list(self.featureset)
322 for feature in features:
323 self.featureset.setFeature(feature)
324 bb.debug(1, "Features set %s (was %s)" % (original_featureset, list(self.featureset)))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500325 if (original_featureset != list(self.featureset)) and self.state != state.error and hasattr(self, "data"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500326 self.reset()
327
328 def initConfigurationData(self):
329
330 self.state = state.initial
331 self.caches_array = []
332
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500333 # Need to preserve BB_CONSOLELOG over resets
334 consolelog = None
335 if hasattr(self, "data"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500336 consolelog = self.data.getVar("BB_CONSOLELOG")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500337
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500338 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
339 self.enableDataTracking()
340
341 all_extra_cache_names = []
342 # We hardcode all known cache types in a single place, here.
343 if CookerFeatures.HOB_EXTRA_CACHES in self.featureset:
344 all_extra_cache_names.append("bb.cache_extra:HobRecipeInfo")
345
346 caches_name_array = ['bb.cache:CoreRecipeInfo'] + all_extra_cache_names
347
348 # At least CoreRecipeInfo will be loaded, so caches_array will never be empty!
349 # This is the entry point, no further check needed!
350 for var in caches_name_array:
351 try:
352 module_name, cache_name = var.split(':')
353 module = __import__(module_name, fromlist=(cache_name,))
354 self.caches_array.append(getattr(module, cache_name))
355 except ImportError as exc:
356 logger.critical("Unable to import extra RecipeInfo '%s' from '%s': %s" % (cache_name, module_name, exc))
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500357 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500358
359 self.databuilder = bb.cookerdata.CookerDataBuilder(self.configuration, False)
360 self.databuilder.parseBaseConfiguration()
361 self.data = self.databuilder.data
362 self.data_hash = self.databuilder.data_hash
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500363 self.extraconfigdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500364
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500365 if consolelog:
366 self.data.setVar("BB_CONSOLELOG", consolelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500367
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500368 self.data.setVar('BB_CMDLINE', self.ui_cmdline)
369
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500370 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
371 self.disableDataTracking()
372
Brad Bishop15ae2502019-06-18 21:44:24 -0400373 for mc in self.databuilder.mcdata.values():
374 mc.renameVar("__depends", "__base_depends")
375 self.add_filewatch(mc.getVar("__base_depends", False), self.configwatcher)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500376
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500377 self.baseconfig_valid = True
378 self.parsecache_valid = False
379
380 def handlePRServ(self):
381 # Setup a PR Server based on the new configuration
382 try:
383 self.prhost = prserv.serv.auto_start(self.data)
384 except prserv.serv.PRServiceConfigError as e:
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500385 bb.fatal("Unable to start PR Server, exiting, check the bitbake-cookerdaemon.log")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500386
Brad Bishopa34c0302019-09-23 22:34:48 -0400387 if self.data.getVar("BB_HASHSERVE") == "auto":
388 # Create a new hash server bound to a unix domain socket
Brad Bishop08902b02019-08-20 09:16:51 -0400389 if not self.hashserv:
390 dbfile = (self.data.getVar("PERSISTENT_DIR") or self.data.getVar("CACHE")) + "/hashserv.db"
Andrew Geissler595f6302022-01-24 19:11:47 +0000391 upstream = self.data.getVar("BB_HASHSERVE_UPSTREAM") or None
392 if upstream:
393 import socket
394 try:
395 sock = socket.create_connection(upstream.split(":"), 5)
396 sock.close()
397 except socket.error as e:
398 bb.warn("BB_HASHSERVE_UPSTREAM is not valid, unable to connect hash equivalence server at '%s': %s"
399 % (upstream, repr(e)))
400
Brad Bishopa34c0302019-09-23 22:34:48 -0400401 self.hashservaddr = "unix://%s/hashserve.sock" % self.data.getVar("TOPDIR")
Andrew Geissler5199d832021-09-24 16:47:35 -0500402 self.hashserv = hashserv.create_server(
403 self.hashservaddr,
404 dbfile,
405 sync=False,
Andrew Geissler595f6302022-01-24 19:11:47 +0000406 upstream=upstream,
Andrew Geissler5199d832021-09-24 16:47:35 -0500407 )
Patrick Williams213cb262021-08-07 19:21:33 -0500408 self.hashserv.serve_as_process()
Brad Bishopa34c0302019-09-23 22:34:48 -0400409 self.data.setVar("BB_HASHSERVE", self.hashservaddr)
410 self.databuilder.origdata.setVar("BB_HASHSERVE", self.hashservaddr)
411 self.databuilder.data.setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400412 for mc in self.databuilder.mcdata:
Brad Bishopa34c0302019-09-23 22:34:48 -0400413 self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
Brad Bishop08902b02019-08-20 09:16:51 -0400414
415 bb.parse.init_parser(self.data)
416
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500417 def enableDataTracking(self):
418 self.configuration.tracking = True
419 if hasattr(self, "data"):
420 self.data.enableTracking()
421
422 def disableDataTracking(self):
423 self.configuration.tracking = False
424 if hasattr(self, "data"):
425 self.data.disableTracking()
426
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500427 def parseConfiguration(self):
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600428 self.updateCacheSync()
429
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500430 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500431 nice = self.data.getVar("BB_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500432 if nice:
433 curnice = os.nice(0)
434 nice = int(nice) - curnice
435 buildlog.verbose("Renice to %s " % os.nice(nice))
436
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600437 if self.recipecaches:
438 del self.recipecaches
439 self.multiconfigs = self.databuilder.mcdata.keys()
440 self.recipecaches = {}
441 for mc in self.multiconfigs:
442 self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500443
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500444 self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500445
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500446 self.parsecache_valid = False
447
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500448 def updateConfigOpts(self, options, environment, cmdline):
449 self.ui_cmdline = cmdline
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500450 clean = True
451 for o in options:
452 if o in ['prefile', 'postfile']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500453 # Only these options may require a reparse
454 try:
455 if getattr(self.configuration, o) == options[o]:
456 # Value is the same, no need to mark dirty
457 continue
458 except AttributeError:
459 pass
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600460 logger.debug("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500461 print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500462 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500463 if hasattr(self.configuration, o):
464 setattr(self.configuration, o, options[o])
465
466 if self.configuration.writeeventlog:
467 if self.eventlog and self.eventlog[0] != self.configuration.writeeventlog:
468 bb.event.unregister_UIHhandler(self.eventlog[1])
469 if not self.eventlog or self.eventlog[0] != self.configuration.writeeventlog:
470 # we log all events to a file if so directed
471 # register the log file writer as UI Handler
472 writer = EventWriter(self, self.configuration.writeeventlog)
473 EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
474 self.eventlog = (self.configuration.writeeventlog, bb.event.register_UIHhandler(EventLogWriteHandler(writer)))
475
476 bb.msg.loggerDefaultLogLevel = self.configuration.default_loglevel
477 bb.msg.loggerDefaultDomains = self.configuration.debug_domains
478
479 if hasattr(self, "data"):
480 origenv = bb.data.init()
481 for k in environment:
482 origenv.setVar(k, environment[k])
483 self.data.setVar("BB_ORIGENV", origenv)
484
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500485 for k in bb.utils.approved_variables():
486 if k in environment and k not in self.configuration.env:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600487 logger.debug("Updating new environment variable %s to %s" % (k, environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488 self.configuration.env[k] = environment[k]
489 clean = False
490 if k in self.configuration.env and k not in environment:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600491 logger.debug("Updating environment variable %s (deleted)" % (k))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500492 del self.configuration.env[k]
493 clean = False
494 if k not in self.configuration.env and k not in environment:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500495 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500496 if environment[k] != self.configuration.env[k]:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600497 logger.debug("Updating environment variable %s from %s to %s" % (k, self.configuration.env[k], environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500498 self.configuration.env[k] = environment[k]
499 clean = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500500
501 # Now update all the variables not in the datastore to match
502 self.configuration.env = environment
503
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500504 if not clean:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600505 logger.debug("Base environment change, triggering reparse")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500506 self.reset()
507
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000508 def runCommands(self, server, data, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500509 """
510 Run any queued asynchronous command
511 This is done by the idle handler so it runs in true context rather than
512 tied to any UI.
513 """
514
515 return self.command.runAsyncCommand()
516
517 def showVersions(self):
518
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500519 (latest_versions, preferred_versions, required) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500520
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500521 logger.plain("%-35s %25s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version", "Required Version")
522 logger.plain("%-35s %25s %25s %25s\n", "===========", "==============", "=================", "================")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500523
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500524 for p in sorted(self.recipecaches[''].pkg_pn):
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500525 preferred = preferred_versions[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500526 latest = latest_versions[p]
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500527 requiredstr = ""
528 preferredstr = ""
529 if required[p]:
530 if preferred[0] is not None:
531 requiredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
532 else:
533 bb.fatal("REQUIRED_VERSION of package %s not available" % p)
534 else:
535 preferredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500536
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500537 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
538
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500539 if preferred == latest:
540 preferredstr = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500541
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500542 logger.plain("%-35s %25s %25s %25s", p, lateststr, preferredstr, requiredstr)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500543
544 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
545 """
546 Show the outer or per-recipe environment
547 """
548 fn = None
549 envdata = None
Brad Bishop15ae2502019-06-18 21:44:24 -0400550 mc = ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500551 if not pkgs_to_build:
552 pkgs_to_build = []
553
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500554 orig_tracking = self.configuration.tracking
555 if not orig_tracking:
556 self.enableDataTracking()
557 self.reset()
558
Brad Bishop15ae2502019-06-18 21:44:24 -0400559 def mc_base(p):
560 if p.startswith('mc:'):
561 s = p.split(':')
562 if len(s) == 2:
563 return s[1]
564 return None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500565
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566 if buildfile:
567 # Parse the configuration here. We need to do it explicitly here since
568 # this showEnvironment() code path doesn't use the cache
569 self.parseConfiguration()
570
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600571 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500572 fn = self.matchFile(fn, mc)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600573 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500574 elif len(pkgs_to_build) == 1:
Brad Bishop15ae2502019-06-18 21:44:24 -0400575 mc = mc_base(pkgs_to_build[0])
576 if not mc:
577 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
578 if pkgs_to_build[0] in set(ignore.split()):
579 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500580
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000581 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.halt, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500582
Brad Bishop15ae2502019-06-18 21:44:24 -0400583 mc = runlist[0][0]
584 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500585
586 if fn:
587 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -0500588 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
589 envdata = bb_caches[mc].loadDataFull(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500590 except Exception as e:
591 parselog.exception("Unable to read %s", fn)
592 raise
Brad Bishop15ae2502019-06-18 21:44:24 -0400593 else:
594 if not mc in self.databuilder.mcdata:
595 bb.fatal('Not multiconfig named "%s" found' % mc)
596 envdata = self.databuilder.mcdata[mc]
597 data.expandKeys(envdata)
598 parse.ast.runAnonFuncs(envdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500599
600 # Display history
601 with closing(StringIO()) as env:
602 self.data.inchistory.emit(env)
603 logger.plain(env.getvalue())
604
605 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500606 with closing(StringIO()) as env:
607 data.emit_env(env, envdata, True)
608 logger.plain(env.getvalue())
609
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000610 # emit the metadata which isn't valid shell
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500611 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600612 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500613 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500614
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500615 if not orig_tracking:
616 self.disableDataTracking()
617 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500618
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000619 def buildTaskData(self, pkgs_to_build, task, halt, allowincomplete=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500620 """
621 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
622 """
623 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
624
625 # A task of None means use the default task
626 if task is None:
627 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500628 if not task.startswith("do_"):
629 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500630
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500631 targetlist = self.checkPackages(pkgs_to_build, task)
632 fulltargetlist = []
633 defaulttask_implicit = ''
634 defaulttask_explicit = False
635 wildcard = False
636
637 # Wild card expansion:
Brad Bishop15ae2502019-06-18 21:44:24 -0400638 # Replace string such as "mc:*:bash"
639 # into "mc:A:bash mc:B:bash bash"
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500640 for k in targetlist:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600641 if k.startswith("mc:") and k.count(':') >= 2:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500642 if wildcard:
643 bb.fatal('multiconfig conflict')
644 if k.split(":")[1] == "*":
645 wildcard = True
646 for mc in self.multiconfigs:
647 if mc:
648 fulltargetlist.append(k.replace('*', mc))
649 # implicit default task
650 else:
651 defaulttask_implicit = k.split(":")[2]
652 else:
653 fulltargetlist.append(k)
654 else:
655 defaulttask_explicit = True
656 fulltargetlist.append(k)
657
658 if not defaulttask_explicit and defaulttask_implicit != '':
659 fulltargetlist.append(defaulttask_implicit)
660
661 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600662 taskdata = {}
663 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500664
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600665 for mc in self.multiconfigs:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000666 taskdata[mc] = bb.taskdata.TaskData(halt, skiplist=self.skiplist, allowincomplete=allowincomplete)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600667 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600668 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500669
670 current = 0
671 runlist = []
672 for k in fulltargetlist:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500673 origk = k
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600674 mc = ""
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600675 if k.startswith("mc:") and k.count(':') >= 2:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600676 mc = k.split(":")[1]
677 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500678 ktask = task
679 if ":do_" in k:
680 k2 = k.split(":do_")
681 k = k2[0]
682 ktask = k2[1]
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500683
684 if mc not in self.multiconfigs:
685 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named %s" % (origk, mc))
686
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600687 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500688 current += 1
689 if not ktask.startswith("do_"):
690 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600691 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
692 # e.g. in ASSUME_PROVIDED
693 continue
694 fn = taskdata[mc].build_targets[k][0]
695 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500696 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600697
Brad Bishop15ae2502019-06-18 21:44:24 -0400698 havemc = False
699 for mc in self.multiconfigs:
700 if taskdata[mc].get_mcdepends():
701 havemc = True
Brad Bishopf058f492019-01-28 23:50:33 -0500702
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800703 # No need to do check providers if there are no mcdeps or not an mc build
Brad Bishop15ae2502019-06-18 21:44:24 -0400704 if havemc or len(self.multiconfigs) > 1:
Andrew Geissler99467da2019-02-25 18:54:23 -0600705 seen = set()
706 new = True
707 # Make sure we can provide the multiconfig dependency
708 while new:
709 mcdeps = set()
710 # Add unresolved first, so we can get multiconfig indirect dependencies on time
711 for mc in self.multiconfigs:
712 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
713 mcdeps |= set(taskdata[mc].get_mcdepends())
714 new = False
715 for mc in self.multiconfigs:
716 for k in mcdeps:
717 if k in seen:
718 continue
719 l = k.split(':')
720 depmc = l[2]
721 if depmc not in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500722 bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named configuration %s" % (k,depmc))
Andrew Geissler99467da2019-02-25 18:54:23 -0600723 else:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600724 logger.debug("Adding providers for multiconfig dependency %s" % l[3])
Andrew Geissler99467da2019-02-25 18:54:23 -0600725 taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
726 seen.add(k)
727 new = True
Brad Bishopf058f492019-01-28 23:50:33 -0500728
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600729 for mc in self.multiconfigs:
730 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
731
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500732 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600733 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500734
735 def prepareTreeData(self, pkgs_to_build, task):
736 """
737 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
738 """
739
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000740 # We set halt to False here to prevent unbuildable targets raising
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500741 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600742 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500743
744 return runlist, taskdata
745
746 ######## WARNING : this function requires cache_extra to be enabled ########
747
748 def generateTaskDepTreeData(self, pkgs_to_build, task):
749 """
750 Create a dependency graph of pkgs_to_build including reverse dependency
751 information.
752 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500753 if not task.startswith("do_"):
754 task = "do_%s" % task
755
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600757 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500758 rq.rqdata.prepare()
759 return self.buildDependTree(rq, taskdata)
760
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600761 @staticmethod
762 def add_mc_prefix(mc, pn):
763 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -0400764 return "mc:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600765 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500766
767 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600768 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500769 depend_tree = {}
770 depend_tree["depends"] = {}
771 depend_tree["tdepends"] = {}
772 depend_tree["pn"] = {}
773 depend_tree["rdepends-pn"] = {}
774 depend_tree["packages"] = {}
775 depend_tree["rdepends-pkg"] = {}
776 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500777 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600778 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600780 for mc in taskdata:
781 for name, fn in list(taskdata[mc].get_providermap().items()):
782 pn = self.recipecaches[mc].pkg_fn[fn]
783 pn = self.add_mc_prefix(mc, pn)
784 if name != pn:
785 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
786 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500787
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600788 for tid in rq.rqdata.runtaskentries:
789 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
790 pn = self.recipecaches[mc].pkg_fn[taskfn]
791 pn = self.add_mc_prefix(mc, pn)
792 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500793 if pn not in depend_tree["pn"]:
794 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600795 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500796 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600797 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500798
799 # if we have extra caches, list all attributes they bring in
800 extra_info = []
801 for cache_class in self.caches_array:
802 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
803 cachefields = getattr(cache_class, 'cachefields', [])
804 extra_info = extra_info + cachefields
805
806 # for all attributes stored, add them to the dependency tree
807 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600808 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500809
810
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500811 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
812 if not dotname in depend_tree["tdepends"]:
813 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600814 for dep in rq.rqdata.runtaskentries[tid].depends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800815 (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
816 deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
Andrew Geissler595f6302022-01-24 19:11:47 +0000817 if depmc:
818 depmc = "mc:" + depmc + ":"
819 depend_tree["tdepends"][dotname].append("%s%s.%s" % (depmc, deppn, bb.runqueue.taskname_from_tid(dep)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600820 if taskfn not in seen_fns:
821 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500822 packages = []
823
824 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600825 for dep in taskdata[mc].depids[taskfn]:
826 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500827
828 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600829 for rdep in taskdata[mc].rdepids[taskfn]:
830 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500831
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600832 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833 for package in rdepends:
834 depend_tree["rdepends-pkg"][package] = []
835 for rdepend in rdepends[package]:
836 depend_tree["rdepends-pkg"][package].append(rdepend)
837 packages.append(package)
838
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600839 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500840 for package in rrecs:
841 depend_tree["rrecs-pkg"][package] = []
842 for rdepend in rrecs[package]:
843 depend_tree["rrecs-pkg"][package].append(rdepend)
844 if not package in packages:
845 packages.append(package)
846
847 for package in packages:
848 if package not in depend_tree["packages"]:
849 depend_tree["packages"][package] = {}
850 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600851 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500852 depend_tree["packages"][package]["version"] = version
853
854 return depend_tree
855
856 ######## WARNING : this function requires cache_extra to be enabled ########
857 def generatePkgDepTreeData(self, pkgs_to_build, task):
858 """
859 Create a dependency tree of pkgs_to_build, returning the data.
860 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500861 if not task.startswith("do_"):
862 task = "do_%s" % task
863
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500864 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500865
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600866 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500867 depend_tree = {}
868 depend_tree["depends"] = {}
869 depend_tree["pn"] = {}
870 depend_tree["rdepends-pn"] = {}
871 depend_tree["rdepends-pkg"] = {}
872 depend_tree["rrecs-pkg"] = {}
873
874 # if we have extra caches, list all attributes they bring in
875 extra_info = []
876 for cache_class in self.caches_array:
877 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
878 cachefields = getattr(cache_class, 'cachefields', [])
879 extra_info = extra_info + cachefields
880
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600881 tids = []
882 for mc in taskdata:
883 for tid in taskdata[mc].taskentries:
884 tids.append(tid)
885
886 for tid in tids:
887 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
888
889 pn = self.recipecaches[mc].pkg_fn[taskfn]
890 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500891
892 if pn not in depend_tree["pn"]:
893 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600894 depend_tree["pn"][pn]["filename"] = taskfn
895 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500896 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600897 rdepends = self.recipecaches[mc].rundeps[taskfn]
898 rrecs = self.recipecaches[mc].runrecs[taskfn]
899 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500900
901 # for all extra attributes stored, add them to the dependency tree
902 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600903 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500904
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600905 if taskfn not in seen_fns:
906 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500907
908 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500909 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500910 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600911 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
912 fn_provider = taskdata[mc].build_targets[dep][0]
913 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500914 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500915 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600916 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500917 depend_tree["depends"][pn].append(pn_provider)
918
919 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600920 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500921 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600922 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
923 fn_rprovider = taskdata[mc].run_targets[rdep][0]
924 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500925 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600926 pn_rprovider = rdep
927 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500928 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
929
930 depend_tree["rdepends-pkg"].update(rdepends)
931 depend_tree["rrecs-pkg"].update(rrecs)
932
933 return depend_tree
934
935 def generateDepTreeEvent(self, pkgs_to_build, task):
936 """
937 Create a task dependency graph of pkgs_to_build.
938 Generate an event with the result
939 """
940 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
941 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
942
943 def generateDotGraphFiles(self, pkgs_to_build, task):
944 """
945 Create a task dependency graph of pkgs_to_build.
946 Save the result to a set of .dot files.
947 """
948
949 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
950
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500951 with open('pn-buildlist', 'w') as f:
952 for pn in depgraph["pn"]:
953 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500954 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500955
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500956 # Remove old format output files to ensure no confusion with stale data
957 try:
958 os.unlink('pn-depends.dot')
959 except FileNotFoundError:
960 pass
961 try:
962 os.unlink('package-depends.dot')
963 except FileNotFoundError:
964 pass
Brad Bishop79641f22019-09-10 07:20:22 -0400965 try:
966 os.unlink('recipe-depends.dot')
967 except FileNotFoundError:
968 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500969
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500970 with open('task-depends.dot', 'w') as f:
971 f.write("digraph depends {\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400972 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500973 (pn, taskname) = task.rsplit(".", 1)
974 fn = depgraph["pn"][pn]["filename"]
975 version = depgraph["pn"][pn]["version"]
976 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 -0400977 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500978 f.write('"%s" -> "%s"\n' % (task, dep))
979 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500980 logger.info("Task dependencies saved to 'task-depends.dot'")
981
982 def show_appends_with_no_recipes(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500983 appends_without_recipes = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500984 # Determine which bbappends haven't been applied
Andrew Geissler5a43b432020-06-13 10:46:56 -0500985 for mc in self.multiconfigs:
986 # First get list of recipes, including skipped
987 recipefns = list(self.recipecaches[mc].pkg_fn.keys())
988 recipefns.extend(self.skiplist.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500989
Andrew Geissler5a43b432020-06-13 10:46:56 -0500990 # Work out list of bbappends that have been applied
991 applied_appends = []
992 for fn in recipefns:
993 applied_appends.extend(self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500994
Andrew Geissler5a43b432020-06-13 10:46:56 -0500995 appends_without_recipes[mc] = []
996 for _, appendfn in self.collections[mc].bbappends:
997 if not appendfn in applied_appends:
998 appends_without_recipes[mc].append(appendfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500999
Andrew Geissler5a43b432020-06-13 10:46:56 -05001000 msgs = []
1001 for mc in sorted(appends_without_recipes.keys()):
1002 if appends_without_recipes[mc]:
1003 msgs.append('No recipes in %s available for:\n %s' % (mc if mc else 'default',
1004 '\n '.join(appends_without_recipes[mc])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001005
Andrew Geissler5a43b432020-06-13 10:46:56 -05001006 if msgs:
1007 msg = "\n".join(msgs)
1008 warn_only = self.databuilder.mcdata[mc].getVar("BB_DANGLINGAPPENDS_WARNONLY", \
1009 False) or "no"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001010 if warn_only.lower() in ("1", "yes", "true"):
1011 bb.warn(msg)
1012 else:
1013 bb.fatal(msg)
1014
1015 def handlePrefProviders(self):
1016
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001017 for mc in self.multiconfigs:
1018 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001019 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001020
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001021 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001022 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001023 try:
1024 (providee, provider) = p.split(':')
1025 except:
1026 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
1027 continue
1028 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
1029 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
1030 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001031
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001032 def findConfigFilePath(self, configfile):
1033 """
1034 Find the location on disk of configfile and if it exists and was parsed by BitBake
1035 emit the ConfigFilePathFound event with the path to the file.
1036 """
1037 path = bb.cookerdata.findConfigFile(configfile, self.data)
1038 if not path:
1039 return
1040
1041 # Generate a list of parsed configuration files by searching the files
1042 # listed in the __depends and __base_depends variables with a .conf suffix.
1043 conffiles = []
1044 dep_files = self.data.getVar('__base_depends', False) or []
1045 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
1046
1047 for f in dep_files:
1048 if f[0].endswith(".conf"):
1049 conffiles.append(f[0])
1050
1051 _, conf, conffile = path.rpartition("conf/")
1052 match = os.path.join(conf, conffile)
1053 # Try and find matches for conf/conffilename.conf as we don't always
1054 # have the full path to the file.
1055 for cfg in conffiles:
1056 if cfg.endswith(match):
1057 bb.event.fire(bb.event.ConfigFilePathFound(path),
1058 self.data)
1059 break
1060
1061 def findFilesMatchingInDir(self, filepattern, directory):
1062 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001063 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001064 'directory' in each BBPATH. i.e. to find all rootfs package classes available
1065 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
1066 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001067 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001068 """
1069
1070 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001071 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001072 for path in bbpaths:
1073 dirpath = os.path.join(path, directory)
1074 if os.path.exists(dirpath):
1075 for root, dirs, files in os.walk(dirpath):
1076 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001077 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001078 matches.append(f)
1079
1080 if matches:
1081 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1082
Patrick Williams93c203f2021-10-06 16:15:23 -05001083 def testCookerCommandEvent(self, filepattern):
1084 # Dummy command used by OEQA selftest to test tinfoil without IO
1085 matches = ["A", "B"]
1086 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1087
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001088 def findProviders(self, mc=''):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001089 return bb.providers.findProviders(self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001090
1091 def findBestProvider(self, pn, mc=''):
1092 if pn in self.recipecaches[mc].providers:
1093 filenames = self.recipecaches[mc].providers[pn]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001094 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.databuilder.mcdata[mc], self.recipecaches[mc])
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001095 if eligible is not None:
1096 filename = eligible[0]
1097 else:
1098 filename = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001099 return None, None, None, filename
1100 elif pn in self.recipecaches[mc].pkg_pn:
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001101 (latest, latest_f, preferred_ver, preferred_file, required) = bb.providers.findBestProvider(pn, self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
1102 if required and preferred_file is None:
1103 return None, None, None, None
1104 return (latest, latest_f, preferred_ver, preferred_file)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001105 else:
1106 return None, None, None, None
1107
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001108 def findConfigFiles(self, varname):
1109 """
1110 Find config files which are appropriate values for varname.
1111 i.e. MACHINE, DISTRO
1112 """
1113 possible = []
1114 var = varname.lower()
1115
1116 data = self.data
1117 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001118 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001119 for path in bbpaths:
1120 confpath = os.path.join(path, "conf", var)
1121 if os.path.exists(confpath):
1122 for root, dirs, files in os.walk(confpath):
1123 # get all child files, these are appropriate values
1124 for f in files:
1125 val, sep, end = f.rpartition('.')
1126 if end == 'conf':
1127 possible.append(val)
1128
1129 if possible:
1130 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1131
1132 def findInheritsClass(self, klass):
1133 """
1134 Find all recipes which inherit the specified class
1135 """
1136 pkg_list = []
1137
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001138 for pfn in self.recipecaches[''].pkg_fn:
1139 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001140 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001141 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001142
1143 return pkg_list
1144
1145 def generateTargetsTree(self, klass=None, pkgs=None):
1146 """
1147 Generate a dependency tree of buildable targets
1148 Generate an event with the result
1149 """
1150 # if the caller hasn't specified a pkgs list default to universe
1151 if not pkgs:
1152 pkgs = ['universe']
1153 # if inherited_class passed ensure all recipes which inherit the
1154 # specified class are included in pkgs
1155 if klass:
1156 extra_pkgs = self.findInheritsClass(klass)
1157 pkgs = pkgs + extra_pkgs
1158
1159 # generate a dependency tree for all our packages
1160 tree = self.generatePkgDepTreeData(pkgs, 'build')
1161 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1162
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001163 def interactiveMode( self ):
1164 """Drop off into a shell"""
1165 try:
1166 from bb import shell
1167 except ImportError:
1168 parselog.exception("Interactive mode not available")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001169 raise bb.BBHandledException()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001170 else:
1171 shell.start( self )
1172
1173
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001174 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001175 """Handle collections"""
1176 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001177 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001178 if collections:
1179 collection_priorities = {}
1180 collection_depends = {}
1181 collection_list = collections.split()
1182 min_prio = 0
1183 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001184 bb.debug(1,'Processing %s in collection list' % (c))
1185
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001186 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001187 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001188 if priority:
1189 try:
1190 prio = int(priority)
1191 except ValueError:
1192 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1193 errors = True
1194 if min_prio == 0 or prio < min_prio:
1195 min_prio = prio
1196 collection_priorities[c] = prio
1197 else:
1198 collection_priorities[c] = None
1199
1200 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001201 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001202 if deps:
1203 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001204 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001205 except bb.utils.VersionStringException as vse:
1206 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001207 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001208 if dep in collection_list:
1209 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001210 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001211 (op, depver) = opstr.split()
1212 if layerver:
1213 try:
1214 res = bb.utils.vercmp_string_op(layerver, depver, op)
1215 except bb.utils.VersionStringException as vse:
1216 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1217 if not res:
1218 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)
1219 errors = True
1220 else:
1221 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)
1222 errors = True
1223 else:
1224 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1225 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001226 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001227 else:
1228 collection_depends[c] = []
1229
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001230 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001231 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001232 if recs:
1233 try:
1234 recDict = bb.utils.explode_dep_versions2(recs)
1235 except bb.utils.VersionStringException as vse:
1236 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1237 for rec, oplist in list(recDict.items()):
1238 if rec in collection_list:
1239 if oplist:
1240 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001241 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001242 if layerver:
1243 (op, recver) = opstr.split()
1244 try:
1245 res = bb.utils.vercmp_string_op(layerver, recver, op)
1246 except bb.utils.VersionStringException as vse:
1247 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1248 if not res:
1249 parselog.debug(3,"Layer '%s' recommends version %s of layer '%s', but version %s is currently enabled in your configuration. Check that you are using the correct matching versions/branches of these two layers.", c, opstr, rec, layerver)
1250 continue
1251 else:
1252 parselog.debug(3,"Layer '%s' recommends version %s of layer '%s', which exists in your configuration but does not specify a version. Check that you are using the correct matching versions/branches of these two layers.", c, opstr, rec)
1253 continue
1254 parselog.debug(3,"Layer '%s' recommends layer '%s', so we are adding it", c, rec)
1255 collection_depends[c].append(rec)
1256 else:
1257 parselog.debug(3,"Layer '%s' recommends layer '%s', but this layer is not enabled in your configuration", c, rec)
1258
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001259 # Recursively work out collection priorities based on dependencies
1260 def calc_layer_priority(collection):
1261 if not collection_priorities[collection]:
1262 max_depprio = min_prio
1263 for dep in collection_depends[collection]:
1264 calc_layer_priority(dep)
1265 depprio = collection_priorities[dep]
1266 if depprio > max_depprio:
1267 max_depprio = depprio
1268 max_depprio += 1
1269 parselog.debug(1, "Calculated priority of layer %s as %d", collection, max_depprio)
1270 collection_priorities[collection] = max_depprio
1271
1272 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1273 for c in collection_list:
1274 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001275 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001276 if regex is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001277 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1278 errors = True
1279 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001280 elif regex == "":
1281 parselog.debug(1, "BBFILE_PATTERN_%s is empty" % c)
Brad Bishop19323692019-04-05 15:28:33 -04001282 cre = re.compile('^NULL$')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001283 errors = False
1284 else:
1285 try:
1286 cre = re.compile(regex)
1287 except re.error:
1288 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1289 errors = True
1290 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001291 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001292 if errors:
1293 # We've already printed the actual error(s)
1294 raise CollectionError("Errors during parsing layer configuration")
1295
1296 def buildSetVars(self):
1297 """
1298 Setup any variables needed before starting a build
1299 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001300 t = time.gmtime()
1301 for mc in self.databuilder.mcdata:
1302 ds = self.databuilder.mcdata[mc]
1303 if not ds.getVar("BUILDNAME", False):
1304 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1305 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1306 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1307 ds.setVar("TIME", time.strftime('%H%M%S', t))
1308
1309 def reset_mtime_caches(self):
1310 """
1311 Reset mtime caches - this is particularly important when memory resident as something
1312 which is cached is not unlikely to have changed since the last invocation (e.g. a
1313 file associated with a recipe might have been modified by the user).
1314 """
1315 build.reset_cache()
1316 bb.fetch._checksum_cache.mtime_cache.clear()
1317 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1318 if siggen_cache:
1319 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001320
Andrew Geissler5a43b432020-06-13 10:46:56 -05001321 def matchFiles(self, bf, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001322 """
1323 Find the .bb files which match the expression in 'buildfile'.
1324 """
1325 if bf.startswith("/") or bf.startswith("../"):
1326 bf = os.path.abspath(bf)
1327
Andrew Geissler5a43b432020-06-13 10:46:56 -05001328 self.collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)}
1329 filelist, masked, searchdirs = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001330 try:
1331 os.stat(bf)
1332 bf = os.path.abspath(bf)
1333 return [bf]
1334 except OSError:
1335 regexp = re.compile(bf)
1336 matches = []
1337 for f in filelist:
1338 if regexp.search(f) and os.path.isfile(f):
1339 matches.append(f)
1340 return matches
1341
Andrew Geissler5a43b432020-06-13 10:46:56 -05001342 def matchFile(self, buildfile, mc=''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001343 """
1344 Find the .bb file which matches the expression in 'buildfile'.
1345 Raise an error if multiple files
1346 """
Andrew Geissler5a43b432020-06-13 10:46:56 -05001347 matches = self.matchFiles(buildfile, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001348 if len(matches) != 1:
1349 if matches:
1350 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1351 if matches:
1352 for f in matches:
1353 msg += "\n %s" % f
1354 parselog.error(msg)
1355 else:
1356 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1357 raise NoSpecificMatch
1358 return matches[0]
1359
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001360 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001361 """
1362 Build the file matching regexp buildfile
1363 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001364 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001365
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001366 # Too many people use -b because they think it's how you normally
1367 # specify a target to be built, so show a warning
1368 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1369
1370 self.buildFileInternal(buildfile, task)
1371
1372 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1373 """
1374 Build the file matching regexp buildfile
1375 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001376
1377 # Parse the configuration here. We need to do it explicitly here since
1378 # buildFile() doesn't use the cache
1379 self.parseConfiguration()
1380
1381 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001382 if task is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001383 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001384 if not task.startswith("do_"):
1385 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001386
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001387 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001388 fn = self.matchFile(fn, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001389
1390 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001391 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001392
Andrew Geissler5a43b432020-06-13 10:46:56 -05001393 bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001394
Andrew Geissler5a43b432020-06-13 10:46:56 -05001395 infos = bb_caches[mc].parse(fn, self.collections[mc].get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001396 infos = dict(infos)
1397
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001398 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001399 try:
1400 info_array = infos[fn]
1401 except KeyError:
1402 bb.fatal("%s does not exist" % fn)
1403
1404 if info_array[0].skipped:
1405 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1406
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001407 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001408
1409 # Tweak some variables
1410 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001411 self.recipecaches[mc].ignored_dependencies = set()
1412 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001413 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001414
1415 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001416 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1417 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001418 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1419 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001420
1421 # Invalidate task for target if force mode active
1422 if self.configuration.force:
1423 logger.verbose("Invalidate task %s, %s", task, fn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001424 bb.parse.siggen.invalidate_task(task, self.recipecaches[mc], fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001425
1426 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001427 taskdata = {}
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001428 taskdata[mc] = bb.taskdata.TaskData(self.configuration.halt)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001429 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001430
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001431 if quietlog:
1432 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1433 bb.runqueue.logger.setLevel(logging.WARNING)
1434
1435 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1436 if fireevents:
1437 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001438
1439 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001440 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001441
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001442 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001443
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001444 def buildFileIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001445
1446 msg = None
1447 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001448 if halt or self.state == state.forceshutdown:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001449 rq.finish_runqueue(True)
1450 msg = "Forced shutdown"
1451 interrupted = 2
1452 elif self.state == state.shutdown:
1453 rq.finish_runqueue(False)
1454 msg = "Stopped build"
1455 interrupted = 1
1456 failures = 0
1457 try:
1458 retval = rq.execute_runqueue()
1459 except runqueue.TaskFailure as exc:
1460 failures += len(exc.args)
1461 retval = False
1462 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001463 self.command.finishAsyncCommand(str(exc))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001464 if quietlog:
1465 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001466 return False
1467
1468 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001469 if fireevents:
1470 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, item, failures, interrupted), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001471 self.command.finishAsyncCommand(msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001472 # We trashed self.recipecaches above
1473 self.parsecache_valid = False
1474 self.configuration.limited_deps = False
1475 bb.parse.siggen.reset(self.data)
1476 if quietlog:
1477 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001478 return False
1479 if retval is True:
1480 return True
1481 return retval
1482
Andrew Geissler635e0e42020-08-21 15:58:33 -05001483 self.idleCallBackRegister(buildFileIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001484
1485 def buildTargets(self, targets, task):
1486 """
1487 Attempt to build the targets specified
1488 """
1489
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001490 def buildTargetsIdle(server, rq, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001491 msg = None
1492 interrupted = 0
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001493 if halt or self.state == state.forceshutdown:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001494 rq.finish_runqueue(True)
1495 msg = "Forced shutdown"
1496 interrupted = 2
1497 elif self.state == state.shutdown:
1498 rq.finish_runqueue(False)
1499 msg = "Stopped build"
1500 interrupted = 1
1501 failures = 0
1502 try:
1503 retval = rq.execute_runqueue()
1504 except runqueue.TaskFailure as exc:
1505 failures += len(exc.args)
1506 retval = False
1507 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001508 self.command.finishAsyncCommand(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001509 return False
1510
1511 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001512 try:
1513 for mc in self.multiconfigs:
1514 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1515 finally:
1516 self.command.finishAsyncCommand(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001517 return False
1518 if retval is True:
1519 return True
1520 return retval
1521
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001522 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001523 self.buildSetVars()
1524
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001525 # If we are told to do the None task then query the default task
Andrew Geissler82c905d2020-04-13 13:39:40 -05001526 if task is None:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001527 task = self.configuration.cmd
1528
1529 if not task.startswith("do_"):
1530 task = "do_%s" % task
1531
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001532 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1533
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001534 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001535
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001536 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.halt)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001537
1538 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001539
1540 # make targets to always look as <target>:do_<task>
1541 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001542 for target in runlist:
1543 if target[0]:
Brad Bishop15ae2502019-06-18 21:44:24 -04001544 ntargets.append("mc:%s:%s:%s" % (target[0], target[1], target[2]))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001545 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001546
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001547 for mc in self.multiconfigs:
1548 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001549
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001550 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001551 if 'universe' in targets:
1552 rq.rqdata.warn_multi_bb = True
1553
Andrew Geissler635e0e42020-08-21 15:58:33 -05001554 self.idleCallBackRegister(buildTargetsIdle, rq)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001555
1556
1557 def getAllKeysWithFlags(self, flaglist):
1558 dump = {}
1559 for k in self.data.keys():
1560 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001561 expand = True
1562 flags = self.data.getVarFlags(k)
1563 if flags and "func" in flags and "python" in flags:
1564 expand = False
1565 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001566 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1567 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001568 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001569 'history' : self.data.varhistory.variable(k),
1570 }
1571 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001572 if flags and d in flags:
1573 dump[k][d] = flags[d]
1574 else:
1575 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001576 except Exception as e:
1577 print(e)
1578 return dump
1579
1580
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001581 def updateCacheSync(self):
1582 if self.state == state.running:
1583 return
1584
1585 # reload files for which we got notifications
1586 for p in self.inotify_modified_files:
1587 bb.parse.update_cache(p)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001588 if p in bb.parse.BBHandler.cached_statements:
1589 del bb.parse.BBHandler.cached_statements[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001590 self.inotify_modified_files = []
1591
1592 if not self.baseconfig_valid:
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001593 logger.debug("Reloading base configuration data")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001594 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001595 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001596
1597 # This is called for all async commands when self.state != running
1598 def updateCache(self):
1599 if self.state == state.running:
1600 return
1601
1602 if self.state in (state.shutdown, state.forceshutdown, state.error):
1603 if hasattr(self.parser, 'shutdown'):
1604 self.parser.shutdown(clean=False, force = True)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001605 self.parser.final_cleanup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001606 raise bb.BBHandledException()
1607
1608 if self.state != state.parsing:
1609 self.updateCacheSync()
1610
1611 if self.state != state.parsing and not self.parsecache_valid:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001612 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001613 self.parseConfiguration ()
1614 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001615 for mc in self.multiconfigs:
1616 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001617
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001618 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001619 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001620 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001621
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001622 for dep in self.configuration.extra_assume_provided:
1623 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001624
Andrew Geissler5a43b432020-06-13 10:46:56 -05001625 self.collections = {}
1626
1627 mcfilelist = {}
1628 total_masked = 0
1629 searchdirs = set()
1630 for mc in self.multiconfigs:
1631 self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc)
1632 (filelist, masked, search) = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
1633
1634 mcfilelist[mc] = filelist
1635 total_masked += masked
1636 searchdirs |= set(search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001637
1638 # Add inotify watches for directories searched for bb/bbappend files
1639 for dirent in searchdirs:
1640 self.add_filewatch([[dirent]], dirs=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001641
Andrew Geissler5a43b432020-06-13 10:46:56 -05001642 self.parser = CookerParser(self, mcfilelist, total_masked)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001643 self.parsecache_valid = True
1644
1645 self.state = state.parsing
1646
1647 if not self.parser.parse_next():
1648 collectlog.debug(1, "parsing complete")
1649 if self.parser.error:
1650 raise bb.BBHandledException()
1651 self.show_appends_with_no_recipes()
1652 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001653 for mc in self.multiconfigs:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001654 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 -05001655 self.state = state.running
1656
1657 # Send an event listing all stamps reachable after parsing
1658 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001659 for mc in self.multiconfigs:
1660 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1661 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001662 return None
1663
1664 return True
1665
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001666 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001667
1668 # Return a copy, don't modify the original
1669 pkgs_to_build = pkgs_to_build[:]
1670
Andrew Geissler595f6302022-01-24 19:11:47 +00001671 if not pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001672 raise NothingToBuild
1673
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001674 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001675 for pkg in pkgs_to_build.copy():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001676 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001677 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Brad Bishop15ae2502019-06-18 21:44:24 -04001678 if pkg.startswith("multiconfig:"):
1679 pkgs_to_build.remove(pkg)
1680 pkgs_to_build.append(pkg.replace("multiconfig:", "mc:"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001681
1682 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001683 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001684 for mc in self.multiconfigs:
1685 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1686 for t in self.recipecaches[mc].world_target:
1687 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001688 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001689 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001690
1691 if 'universe' in pkgs_to_build:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001692 parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001693 parselog.debug(1, "collating packages for \"universe\"")
1694 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001695 for mc in self.multiconfigs:
1696 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001697 if task:
1698 foundtask = False
1699 for provider_fn in self.recipecaches[mc].providers[t]:
1700 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1701 foundtask = True
1702 break
1703 if not foundtask:
1704 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1705 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001706 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -04001707 t = "mc:" + mc + ":" + t
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001708 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001709
1710 return pkgs_to_build
1711
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001712 def pre_serve(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001713 return
1714
1715 def post_serve(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001716 self.shutdown(force=True)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001717 prserv.serv.auto_shutdown()
Brad Bishop08902b02019-08-20 09:16:51 -04001718 if self.hashserv:
1719 self.hashserv.process.terminate()
1720 self.hashserv.process.join()
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001721 if hasattr(self, "data"):
1722 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001723
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001724 def shutdown(self, force = False):
1725 if force:
1726 self.state = state.forceshutdown
1727 else:
1728 self.state = state.shutdown
1729
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001730 if self.parser:
1731 self.parser.shutdown(clean=not force, force=force)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001732 self.parser.final_cleanup()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001733
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001734 def finishcommand(self):
1735 self.state = state.initial
1736
1737 def reset(self):
1738 self.initConfigurationData()
Brad Bishop08902b02019-08-20 09:16:51 -04001739 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001740
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001741 def clientComplete(self):
1742 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001743 self.finishcommand()
1744 self.extraconfigdata = {}
1745 self.command.reset()
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001746 if hasattr(self, "data"):
1747 self.databuilder.reset()
1748 self.data = self.databuilder.data
Andrew Geissler82c905d2020-04-13 13:39:40 -05001749 self.parsecache_valid = False
1750 self.baseconfig_valid = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001751
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001752
1753class CookerExit(bb.event.Event):
1754 """
1755 Notify clients of the Cooker shutdown
1756 """
1757
1758 def __init__(self):
1759 bb.event.Event.__init__(self)
1760
1761
1762class CookerCollectFiles(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001763 def __init__(self, priorities, mc=''):
1764 self.mc = mc
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001765 self.bbappends = []
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001766 # Priorities is a list of tuples, with the second element as the pattern.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001767 # We need to sort the list with the longest pattern first, and so on to
1768 # the shortest. This allows nested layers to be properly evaluated.
1769 self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001770
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001771 def calc_bbfile_priority(self, filename):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001772 for _, _, regex, pri in self.bbfile_config_priorities:
1773 if regex.match(filename):
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001774 return pri, regex
1775 return 0, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001776
1777 def get_bbfiles(self):
1778 """Get list of default .bb files by reading out the current directory"""
1779 path = os.getcwd()
1780 contents = os.listdir(path)
1781 bbfiles = []
1782 for f in contents:
1783 if f.endswith(".bb"):
1784 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1785 return bbfiles
1786
1787 def find_bbfiles(self, path):
1788 """Find all the .bb and .bbappend files in a directory"""
1789 found = []
1790 for dir, dirs, files in os.walk(path):
1791 for ignored in ('SCCS', 'CVS', '.svn'):
1792 if ignored in dirs:
1793 dirs.remove(ignored)
1794 found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
1795
1796 return found
1797
1798 def collect_bbfiles(self, config, eventdata):
1799 """Collect all available .bb build files"""
1800 masked = 0
1801
1802 collectlog.debug(1, "collecting .bb files")
1803
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001804 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001805
1806 # Sort files by priority
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001807 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem)[0] )
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001808 config.setVar("BBFILES_PRIORITIZED", " ".join(files))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001809
Andrew Geissler595f6302022-01-24 19:11:47 +00001810 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001811 files = self.get_bbfiles()
1812
Andrew Geissler595f6302022-01-24 19:11:47 +00001813 if not files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001814 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1815 bb.event.fire(CookerExit(), eventdata)
1816
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001817 # We need to track where we look so that we can add inotify watches. There
1818 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001819 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001820 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001821 if hasattr(os, 'scandir'):
1822 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001823 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001824
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001825 def ourlistdir(d):
1826 searchdirs.append(d)
1827 return origlistdir(d)
1828
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001829 def ourscandir(d):
1830 searchdirs.append(d)
1831 return origscandir(d)
1832
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001833 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001834 if hasattr(os, 'scandir'):
1835 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001836 try:
1837 # Can't use set here as order is important
1838 newfiles = []
1839 for f in files:
1840 if os.path.isdir(f):
1841 dirfiles = self.find_bbfiles(f)
1842 for g in dirfiles:
1843 if g not in newfiles:
1844 newfiles.append(g)
1845 else:
1846 globbed = glob.glob(f)
1847 if not globbed and os.path.exists(f):
1848 globbed = [f]
1849 # glob gives files in order on disk. Sort to be deterministic.
1850 for g in sorted(globbed):
1851 if g not in newfiles:
1852 newfiles.append(g)
1853 finally:
1854 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001855 if hasattr(os, 'scandir'):
1856 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001857
1858 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001859
1860 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001861 # First validate the individual regular expressions and ignore any
1862 # that do not compile
1863 bbmasks = []
1864 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001865 # When constructing an older style single regex, it's possible for BBMASK
1866 # to end up beginning with '|', which matches and masks _everything_.
1867 if mask.startswith("|"):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001868 collectlog.warning("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001869 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001870 try:
1871 re.compile(mask)
1872 bbmasks.append(mask)
1873 except sre_constants.error:
1874 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1875
1876 # Then validate the combined regular expressions. This should never
1877 # fail, but better safe than sorry...
1878 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001879 try:
1880 bbmask_compiled = re.compile(bbmask)
1881 except sre_constants.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001882 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1883 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001884
1885 bbfiles = []
1886 bbappend = []
1887 for f in newfiles:
1888 if bbmask and bbmask_compiled.search(f):
1889 collectlog.debug(1, "skipping masked file %s", f)
1890 masked += 1
1891 continue
1892 if f.endswith('.bb'):
1893 bbfiles.append(f)
1894 elif f.endswith('.bbappend'):
1895 bbappend.append(f)
1896 else:
1897 collectlog.debug(1, "skipping %s: unknown file extension", f)
1898
1899 # Build a list of .bbappend files for each .bb file
1900 for f in bbappend:
1901 base = os.path.basename(f).replace('.bbappend', '.bb')
1902 self.bbappends.append((base, f))
1903
1904 # Find overlayed recipes
1905 # bbfiles will be in priority order which makes this easy
1906 bbfile_seen = dict()
1907 self.overlayed = defaultdict(list)
1908 for f in reversed(bbfiles):
1909 base = os.path.basename(f)
1910 if base not in bbfile_seen:
1911 bbfile_seen[base] = f
1912 else:
1913 topfile = bbfile_seen[base]
1914 self.overlayed[topfile].append(f)
1915
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001916 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001917
1918 def get_file_appends(self, fn):
1919 """
1920 Returns a list of .bbappend files to apply to fn
1921 """
1922 filelist = []
1923 f = os.path.basename(fn)
1924 for b in self.bbappends:
1925 (bbappend, filename) = b
1926 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1927 filelist.append(filename)
Andrew Geissler5a43b432020-06-13 10:46:56 -05001928 return tuple(filelist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001929
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001930 def collection_priorities(self, pkgfns, fns, d):
1931 # Return the priorities of the entries in pkgfns
1932 # Also check that all the regexes in self.bbfile_config_priorities are used
1933 # (but to do that we need to ensure skipped recipes aren't counted, nor
1934 # collections in BBFILE_PATTERN_IGNORE_EMPTY)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001935
1936 priorities = {}
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001937 seen = set()
1938 matched = set()
1939
1940 matched_regex = set()
1941 unmatched_regex = set()
1942 for _, _, regex, _ in self.bbfile_config_priorities:
1943 unmatched_regex.add(regex)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001944
1945 # Calculate priorities for each file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001946 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001947 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001948 priorities[p], regex = self.calc_bbfile_priority(realfn)
1949 if regex in unmatched_regex:
1950 matched_regex.add(regex)
1951 unmatched_regex.remove(regex)
1952 seen.add(realfn)
1953 if regex:
1954 matched.add(realfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001955
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001956 if unmatched_regex:
1957 # Account for bbappend files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001958 for b in self.bbappends:
1959 (bbfile, append) = b
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001960 seen.add(append)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001961
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001962 # Account for skipped recipes
1963 seen.update(fns)
1964
1965 seen.difference_update(matched)
1966
1967 def already_matched(fn):
1968 for regex in matched_regex:
1969 if regex.match(fn):
1970 return True
1971 return False
1972
1973 for unmatch in unmatched_regex.copy():
1974 for fn in seen:
1975 if unmatch.match(fn):
1976 # If the bbappend or file was already matched by another regex, skip it
1977 # e.g. for a layer within a layer, the outer regex could match, the inner
1978 # regex may match nothing and we should warn about that
1979 if already_matched(fn):
1980 continue
1981 unmatched_regex.remove(unmatch)
1982 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001983
1984 for collection, pattern, regex, _ in self.bbfile_config_priorities:
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001985 if regex in unmatched_regex:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001986 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Andrew Geissler5a43b432020-06-13 10:46:56 -05001987 collectlog.warning("No bb files in %s matched BBFILE_PATTERN_%s '%s'" % (self.mc if self.mc else 'default',
1988 collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001989
1990 return priorities
1991
1992class ParsingFailure(Exception):
1993 def __init__(self, realexception, recipe):
1994 self.realexception = realexception
1995 self.recipe = recipe
1996 Exception.__init__(self, realexception, recipe)
1997
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001998class Parser(multiprocessing.Process):
1999 def __init__(self, jobs, results, quit, init, profile):
2000 self.jobs = jobs
2001 self.results = results
2002 self.quit = quit
2003 self.init = init
2004 multiprocessing.Process.__init__(self)
2005 self.context = bb.utils.get_context().copy()
2006 self.handlers = bb.event.get_class_handlers().copy()
2007 self.profile = profile
2008
2009 def run(self):
2010
2011 if not self.profile:
2012 self.realrun()
2013 return
2014
2015 try:
2016 import cProfile as profile
2017 except:
2018 import profile
2019 prof = profile.Profile()
2020 try:
2021 profile.Profile.runcall(prof, self.realrun)
2022 finally:
2023 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
2024 prof.dump_stats(logfile)
2025
2026 def realrun(self):
2027 if self.init:
2028 self.init()
2029
2030 pending = []
2031 while True:
2032 try:
2033 self.quit.get_nowait()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002034 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002035 pass
2036 else:
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002037 self.results.close()
2038 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002039 break
2040
2041 if pending:
2042 result = pending.pop()
2043 else:
2044 try:
Brad Bishop19323692019-04-05 15:28:33 -04002045 job = self.jobs.pop()
2046 except IndexError:
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002047 self.results.close()
2048 self.results.join_thread()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002049 break
2050 result = self.parse(*job)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002051 # Clear the siggen cache after parsing to control memory usage, its huge
2052 bb.parse.siggen.postparsing_clean_cache()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002053 try:
2054 self.results.put(result, timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002055 except queue.Full:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002056 pending.append(result)
2057
Andrew Geissler5a43b432020-06-13 10:46:56 -05002058 def parse(self, mc, cache, filename, appends):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002059 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05002060 origfilter = bb.event.LogHandler.filter
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002061 # Record the filename we're parsing into any events generated
2062 def parse_filter(self, record):
2063 record.taskpid = bb.event.worker_pid
2064 record.fn = filename
2065 return True
2066
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002067 # Reset our environment and handlers to the original settings
2068 bb.utils.set_context(self.context.copy())
2069 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002070 bb.event.LogHandler.filter = parse_filter
2071
Andrew Geissler5a43b432020-06-13 10:46:56 -05002072 return True, mc, cache.parse(filename, appends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002073 except Exception as exc:
2074 tb = sys.exc_info()[2]
2075 exc.recipe = filename
2076 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
2077 return True, exc
2078 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
2079 # and for example a worker thread doesn't just exit on its own in response to
2080 # a SystemExit event for example.
2081 except BaseException as exc:
2082 return True, ParsingFailure(exc, filename)
Andrew Geissler82c905d2020-04-13 13:39:40 -05002083 finally:
2084 bb.event.LogHandler.filter = origfilter
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002085
2086class CookerParser(object):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002087 def __init__(self, cooker, mcfilelist, masked):
2088 self.mcfilelist = mcfilelist
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002089 self.cooker = cooker
2090 self.cfgdata = cooker.data
2091 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002092 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002093
2094 # Accounting statistics
2095 self.parsed = 0
2096 self.cached = 0
2097 self.error = 0
2098 self.masked = masked
2099
2100 self.skipped = 0
2101 self.virtuals = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002102
2103 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002104 self.process_names = []
2105
Andrew Geissler5a43b432020-06-13 10:46:56 -05002106 self.bb_caches = bb.cache.MulticonfigCache(self.cfgbuilder, self.cfghash, cooker.caches_array)
2107 self.fromcache = set()
2108 self.willparse = set()
2109 for mc in self.cooker.multiconfigs:
2110 for filename in self.mcfilelist[mc]:
2111 appends = self.cooker.collections[mc].get_file_appends(filename)
2112 if not self.bb_caches[mc].cacheValid(filename, appends):
2113 self.willparse.add((mc, self.bb_caches[mc], filename, appends))
2114 else:
2115 self.fromcache.add((mc, self.bb_caches[mc], filename, appends))
2116
2117 self.total = len(self.fromcache) + len(self.willparse)
2118 self.toparse = len(self.willparse)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002119 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002120
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002121 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Andrew Geissler5a43b432020-06-13 10:46:56 -05002122 multiprocessing.cpu_count()), self.toparse)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002123
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002124 self.start()
2125 self.haveshutdown = False
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002126 self.syncthread = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002127
2128 def start(self):
2129 self.results = self.load_cached()
2130 self.processes = []
2131 if self.toparse:
2132 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
2133 def init():
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002134 signal.signal(signal.SIGTERM, signal.SIG_DFL)
2135 signal.signal(signal.SIGHUP, signal.SIG_DFL)
2136 signal.signal(signal.SIGINT, signal.SIG_IGN)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002137 bb.utils.set_process_name(multiprocessing.current_process().name)
2138 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2139 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002140
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002141 self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002142 self.result_queue = multiprocessing.Queue()
Brad Bishop19323692019-04-05 15:28:33 -04002143
2144 def chunkify(lst,n):
2145 return [lst[i::n] for i in range(n)]
Andrew Geissler5a43b432020-06-13 10:46:56 -05002146 self.jobs = chunkify(list(self.willparse), self.num_processes)
Brad Bishop19323692019-04-05 15:28:33 -04002147
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002148 for i in range(0, self.num_processes):
Brad Bishop19323692019-04-05 15:28:33 -04002149 parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, init, self.cooker.configuration.profile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002150 parser.start()
2151 self.process_names.append(parser.name)
2152 self.processes.append(parser)
2153
2154 self.results = itertools.chain(self.results, self.parse_generator())
2155
2156 def shutdown(self, clean=True, force=False):
2157 if not self.toparse:
2158 return
2159 if self.haveshutdown:
2160 return
2161 self.haveshutdown = True
2162
2163 if clean:
2164 event = bb.event.ParseCompleted(self.cached, self.parsed,
2165 self.skipped, self.masked,
2166 self.virtuals, self.error,
2167 self.total)
2168
2169 bb.event.fire(event, self.cfgdata)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002170 else:
2171 bb.error("Parsing halted due to errors")
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002172
2173 for process in self.processes:
2174 self.parser_quit.put(None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002175
Brad Bishop08902b02019-08-20 09:16:51 -04002176 # Cleanup the queue before call process.join(), otherwise there might be
2177 # deadlocks.
2178 while True:
2179 try:
2180 self.result_queue.get(timeout=0.25)
2181 except queue.Empty:
2182 break
2183
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002184 for process in self.processes:
2185 if force:
2186 process.join(.1)
2187 process.terminate()
2188 else:
2189 process.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002190
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002191 self.parser_quit.close()
2192 # Allow data left in the cancel queue to be discarded
2193 self.parser_quit.cancel_join_thread()
2194
Andrew Geissler5a43b432020-06-13 10:46:56 -05002195 def sync_caches():
2196 for c in self.bb_caches.values():
2197 c.sync()
2198
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002199 sync = threading.Thread(target=sync_caches, name="SyncThread")
2200 self.syncthread = sync
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002201 sync.start()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002202 bb.codeparser.parser_cache_savemerge()
2203 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002204 if self.cooker.configuration.profile:
2205 profiles = []
2206 for i in self.process_names:
2207 logfile = "profile-parse-%s.log" % i
2208 if os.path.exists(logfile):
2209 profiles.append(logfile)
2210
2211 pout = "profile-parse.log.processed"
2212 bb.utils.process_profilelog(profiles, pout = pout)
2213 print("Processed parsing statistics saved to %s" % (pout))
2214
Andrew Geisslerc9f78652020-09-18 14:11:35 -05002215 def final_cleanup(self):
2216 if self.syncthread:
2217 self.syncthread.join()
2218
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002219 def load_cached(self):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002220 for mc, cache, filename, appends in self.fromcache:
2221 cached, infos = cache.load(filename, appends)
2222 yield not cached, mc, infos
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002223
2224 def parse_generator(self):
Andrew Geissler595f6302022-01-24 19:11:47 +00002225 empty = False
2226 while self.processes or not empty:
2227 for process in self.processes.copy():
2228 if not process.is_alive():
2229 process.join()
2230 self.processes.remove(process)
2231
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002232 if self.parsed >= self.toparse:
2233 break
2234
2235 try:
2236 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002237 except queue.Empty:
Andrew Geissler595f6302022-01-24 19:11:47 +00002238 empty = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002239 pass
2240 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00002241 empty = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002242 value = result[1]
2243 if isinstance(value, BaseException):
2244 raise value
2245 else:
2246 yield result
2247
Andrew Geissler595f6302022-01-24 19:11:47 +00002248 if not (self.parsed >= self.toparse):
2249 raise bb.parse.ParseError("Not all recipes parsed, parser thread killed/died? Exiting.", None)
2250
2251
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002252 def parse_next(self):
2253 result = []
2254 parsed = None
2255 try:
Andrew Geissler5a43b432020-06-13 10:46:56 -05002256 parsed, mc, result = next(self.results)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002257 except StopIteration:
2258 self.shutdown()
2259 return False
2260 except bb.BBHandledException as exc:
2261 self.error += 1
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00002262 logger.debug('Failed to parse recipe: %s' % exc.recipe)
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002263 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002264 return False
2265 except ParsingFailure as exc:
2266 self.error += 1
2267 logger.error('Unable to parse %s: %s' %
2268 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002269 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002270 return False
2271 except bb.parse.ParseError as exc:
2272 self.error += 1
2273 logger.error(str(exc))
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002274 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002275 return False
2276 except bb.data_smart.ExpansionError as exc:
2277 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002278 bbdir = os.path.dirname(__file__) + os.sep
2279 etype, value, _ = sys.exc_info()
2280 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2281 logger.error('ExpansionError during parsing %s', value.recipe,
2282 exc_info=(etype, value, tb))
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002283 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002284 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002285 except Exception as exc:
2286 self.error += 1
2287 etype, value, tb = sys.exc_info()
2288 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002289 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002290 exc_info=(etype, value, exc.traceback))
2291 else:
2292 # Most likely, an exception occurred during raising an exception
2293 import traceback
2294 logger.error('Exception during parse: %s' % traceback.format_exc())
Andrew Geissler90fd73c2021-03-05 15:25:55 -06002295 self.shutdown(clean=False, force=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002296 return False
2297
2298 self.current += 1
2299 self.virtuals += len(result)
2300 if parsed:
2301 self.parsed += 1
2302 if self.parsed % self.progress_chunk == 0:
2303 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2304 self.cfgdata)
2305 else:
2306 self.cached += 1
2307
2308 for virtualfn, info_array in result:
2309 if info_array[0].skipped:
2310 self.skipped += 1
2311 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Andrew Geissler5a43b432020-06-13 10:46:56 -05002312 self.bb_caches[mc].add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002313 parsed=parsed, watcher = self.cooker.add_filewatch)
2314 return True
2315
2316 def reparse(self, filename):
Andrew Geissler5a43b432020-06-13 10:46:56 -05002317 to_reparse = set()
2318 for mc in self.cooker.multiconfigs:
2319 to_reparse.add((mc, filename, self.cooker.collections[mc].get_file_appends(filename)))
2320
2321 for mc, filename, appends in to_reparse:
2322 infos = self.bb_caches[mc].parse(filename, appends)
2323 for vfn, info_array in infos:
2324 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)