blob: c8e14042d6a45cfce5eda087c7e49de043f8cfbb [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#!/usr/bin/env python
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002#
3# Copyright (C) 2003, 2004 Chris Larson
4# Copyright (C) 2003, 2004 Phil Blundell
5# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
6# Copyright (C) 2005 Holger Hans Peter Freyther
7# Copyright (C) 2005 ROAD GmbH
8# Copyright (C) 2006 - 2007 Richard Purdie
9#
Brad Bishopc342db32019-05-15 21:57:59 -040010# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011#
Patrick Williamsc0f7c042017-02-23 20:41:17 -060012
Patrick Williamsc124f4f2015-09-15 14:41:29 -050013import sys, os, glob, os.path, re, time
14import atexit
15import itertools
16import logging
17import multiprocessing
18import sre_constants
19import threading
Patrick Williamsc0f7c042017-02-23 20:41:17 -060020from io import StringIO, UnsupportedOperation
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021from contextlib import closing
22from functools import wraps
Patrick Williamsc0f7c042017-02-23 20:41:17 -060023from collections import defaultdict, namedtuple
Patrick Williamsc124f4f2015-09-15 14:41:29 -050024import bb, bb.exceptions, bb.command
25from bb import utils, data, parse, event, cache, providers, taskdata, runqueue, build
Patrick Williamsc0f7c042017-02-23 20:41:17 -060026import queue
Patrick Williamsc124f4f2015-09-15 14:41:29 -050027import signal
28import subprocess
29import errno
30import prserv.serv
31import pyinotify
Patrick Williamsc0f7c042017-02-23 20:41:17 -060032import json
33import pickle
34import codecs
Patrick Williamsc124f4f2015-09-15 14:41:29 -050035
36logger = logging.getLogger("BitBake")
37collectlog = logging.getLogger("BitBake.Collection")
38buildlog = logging.getLogger("BitBake.Build")
39parselog = logging.getLogger("BitBake.Parsing")
40providerlog = logging.getLogger("BitBake.Provider")
41
42class NoSpecificMatch(bb.BBHandledException):
43 """
44 Exception raised when no or multiple file matches are found
45 """
46
47class NothingToBuild(Exception):
48 """
49 Exception raised when there is nothing to build
50 """
51
52class CollectionError(bb.BBHandledException):
53 """
54 Exception raised when layer configuration is incorrect
55 """
56
57class state:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060058 initial, parsing, running, shutdown, forceshutdown, stopped, error = list(range(7))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050059
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050060 @classmethod
61 def get_name(cls, code):
62 for name in dir(cls):
63 value = getattr(cls, name)
64 if type(value) == type(cls.initial) and value == code:
65 return name
66 raise ValueError("Invalid status code: %s" % code)
67
Patrick Williamsc124f4f2015-09-15 14:41:29 -050068
69class SkippedPackage:
70 def __init__(self, info = None, reason = None):
71 self.pn = None
72 self.skipreason = None
73 self.provides = None
74 self.rprovides = None
75
76 if info:
77 self.pn = info.pn
78 self.skipreason = info.skipreason
79 self.provides = info.provides
80 self.rprovides = info.rprovides
81 elif reason:
82 self.skipreason = reason
83
84
85class CookerFeatures(object):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060086 _feature_list = [HOB_EXTRA_CACHES, BASEDATASTORE_TRACKING, SEND_SANITYEVENTS] = list(range(3))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050087
88 def __init__(self):
89 self._features=set()
90
91 def setFeature(self, f):
92 # validate we got a request for a feature we support
93 if f not in CookerFeatures._feature_list:
94 return
95 self._features.add(f)
96
97 def __contains__(self, f):
98 return f in self._features
99
100 def __iter__(self):
101 return self._features.__iter__()
102
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600103 def __next__(self):
104 return next(self._features)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500105
106
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600107class EventWriter:
108 def __init__(self, cooker, eventfile):
109 self.file_inited = None
110 self.cooker = cooker
111 self.eventfile = eventfile
112 self.event_queue = []
113
114 def write_event(self, event):
115 with open(self.eventfile, "a") as f:
116 try:
117 str_event = codecs.encode(pickle.dumps(event), 'base64').decode('utf-8')
118 f.write("%s\n" % json.dumps({"class": event.__module__ + "." + event.__class__.__name__,
119 "vars": str_event}))
120 except Exception as err:
121 import traceback
122 print(err, traceback.format_exc())
123
124 def send(self, event):
125 if self.file_inited:
126 # we have the file, just write the event
127 self.write_event(event)
128 else:
129 # init on bb.event.BuildStarted
130 name = "%s.%s" % (event.__module__, event.__class__.__name__)
131 if name in ("bb.event.BuildStarted", "bb.cooker.CookerExit"):
132 with open(self.eventfile, "w") as f:
133 f.write("%s\n" % json.dumps({ "allvariables" : self.cooker.getAllKeysWithFlags(["doc", "func"])}))
134
135 self.file_inited = True
136
137 # write pending events
138 for evt in self.event_queue:
139 self.write_event(evt)
140
141 # also write the current event
142 self.write_event(event)
143 else:
144 # queue all events until the file is inited
145 self.event_queue.append(event)
146
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147#============================================================================#
148# BBCooker
149#============================================================================#
150class BBCooker:
151 """
152 Manages one bitbake build run
153 """
154
155 def __init__(self, configuration, featureSet=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600156 self.recipecaches = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500157 self.skiplist = {}
158 self.featureset = CookerFeatures()
159 if featureSet:
160 for f in featureSet:
161 self.featureset.setFeature(f)
162
163 self.configuration = configuration
164
Brad Bishopf058f492019-01-28 23:50:33 -0500165 bb.debug(1, "BBCooker starting %s" % time.time())
166 sys.stdout.flush()
167
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500168 self.configwatcher = pyinotify.WatchManager()
Brad Bishopf058f492019-01-28 23:50:33 -0500169 bb.debug(1, "BBCooker pyinotify1 %s" % time.time())
170 sys.stdout.flush()
171
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500172 self.configwatcher.bbseen = []
173 self.configwatcher.bbwatchedfiles = []
174 self.confignotifier = pyinotify.Notifier(self.configwatcher, self.config_notifications)
Brad Bishopf058f492019-01-28 23:50:33 -0500175 bb.debug(1, "BBCooker pyinotify2 %s" % time.time())
176 sys.stdout.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500177 self.watchmask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CREATE | pyinotify.IN_DELETE | \
178 pyinotify.IN_DELETE_SELF | pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF | \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500179 pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500180 self.watcher = pyinotify.WatchManager()
Brad Bishopf058f492019-01-28 23:50:33 -0500181 bb.debug(1, "BBCooker pyinotify3 %s" % time.time())
182 sys.stdout.flush()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500183 self.watcher.bbseen = []
184 self.watcher.bbwatchedfiles = []
185 self.notifier = pyinotify.Notifier(self.watcher, self.notifications)
186
Brad Bishopf058f492019-01-28 23:50:33 -0500187 bb.debug(1, "BBCooker pyinotify complete %s" % time.time())
188 sys.stdout.flush()
189
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500190 # If being called by something like tinfoil, we need to clean cached data
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500191 # which may now be invalid
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500192 bb.parse.clear_cache()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500193 bb.parse.BBHandler.cached_statements = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500194
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500195 self.ui_cmdline = None
196
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500197 self.initConfigurationData()
198
Brad Bishopf058f492019-01-28 23:50:33 -0500199 bb.debug(1, "BBCooker parsed base configuration %s" % time.time())
200 sys.stdout.flush()
201
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600202 # we log all events to a file if so directed
203 if self.configuration.writeeventlog:
204 # register the log file writer as UI Handler
205 writer = EventWriter(self, self.configuration.writeeventlog)
206 EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
207 bb.event.register_UIHhandler(EventLogWriteHandler(writer))
208
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500209 self.inotify_modified_files = []
210
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500211 def _process_inotify_updates(server, cooker, abort):
212 cooker.process_inotify_updates()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500213 return 1.0
214
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500215 self.configuration.server_register_idlecallback(_process_inotify_updates, self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500216
217 # TOSTOP must not be set or our children will hang when they output
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600218 try:
219 fd = sys.stdout.fileno()
220 if os.isatty(fd):
221 import termios
222 tcattr = termios.tcgetattr(fd)
223 if tcattr[3] & termios.TOSTOP:
224 buildlog.info("The terminal had the TOSTOP bit set, clearing...")
225 tcattr[3] = tcattr[3] & ~termios.TOSTOP
226 termios.tcsetattr(fd, termios.TCSANOW, tcattr)
227 except UnsupportedOperation:
228 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500229
230 self.command = bb.command.Command(self)
231 self.state = state.initial
232
233 self.parser = None
234
235 signal.signal(signal.SIGTERM, self.sigterm_exception)
236 # Let SIGHUP exit as SIGTERM
237 signal.signal(signal.SIGHUP, self.sigterm_exception)
238
Brad Bishopf058f492019-01-28 23:50:33 -0500239 bb.debug(1, "BBCooker startup complete %s" % time.time())
240 sys.stdout.flush()
241
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500242 def process_inotify_updates(self):
243 for n in [self.confignotifier, self.notifier]:
244 if n.check_events(timeout=0):
245 # read notified events and enqeue them
246 n.read_events()
247 n.process_events()
248
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500249 def config_notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500250 if event.maskname == "IN_Q_OVERFLOW":
251 bb.warn("inotify event queue overflowed, invalidating caches.")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500252 self.parsecache_valid = False
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500253 self.baseconfig_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500254 bb.parse.clear_cache()
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500255 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500256 if not event.pathname in self.configwatcher.bbwatchedfiles:
257 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500258 if not event.pathname in self.inotify_modified_files:
259 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500260 self.baseconfig_valid = False
261
262 def notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500263 if event.maskname == "IN_Q_OVERFLOW":
264 bb.warn("inotify event queue overflowed, invalidating caches.")
265 self.parsecache_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500266 bb.parse.clear_cache()
267 return
268 if event.pathname.endswith("bitbake-cookerdaemon.log") \
269 or event.pathname.endswith("bitbake.lock"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500270 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500271 if not event.pathname in self.inotify_modified_files:
272 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500273 self.parsecache_valid = False
274
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500275 def add_filewatch(self, deps, watcher=None, dirs=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500276 if not watcher:
277 watcher = self.watcher
278 for i in deps:
279 watcher.bbwatchedfiles.append(i[0])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500280 if dirs:
281 f = i[0]
282 else:
283 f = os.path.dirname(i[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500284 if f in watcher.bbseen:
285 continue
286 watcher.bbseen.append(f)
287 watchtarget = None
288 while True:
289 # We try and add watches for files that don't exist but if they did, would influence
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500290 # the parser. The parent directory of these files may not exist, in which case we need
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500291 # to watch any parent that does exist for changes.
292 try:
293 watcher.add_watch(f, self.watchmask, quiet=False)
294 if watchtarget:
295 watcher.bbwatchedfiles.append(watchtarget)
296 break
297 except pyinotify.WatchManagerError as e:
298 if 'ENOENT' in str(e):
299 watchtarget = f
300 f = os.path.dirname(f)
301 if f in watcher.bbseen:
302 break
303 watcher.bbseen.append(f)
304 continue
305 if 'ENOSPC' in str(e):
306 providerlog.error("No space left on device or exceeds fs.inotify.max_user_watches?")
307 providerlog.error("To check max_user_watches: sysctl -n fs.inotify.max_user_watches.")
308 providerlog.error("To modify max_user_watches: sysctl -n -w fs.inotify.max_user_watches=<value>.")
309 providerlog.error("Root privilege is required to modify max_user_watches.")
310 raise
311
312 def sigterm_exception(self, signum, stackframe):
313 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500314 bb.warn("Cooker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500315 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500316 bb.warn("Cooker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317 self.state = state.forceshutdown
318
319 def setFeatures(self, features):
320 # we only accept a new feature set if we're in state initial, so we can reset without problems
321 if not self.state in [state.initial, state.shutdown, state.forceshutdown, state.stopped, state.error]:
322 raise Exception("Illegal state for feature set change")
323 original_featureset = list(self.featureset)
324 for feature in features:
325 self.featureset.setFeature(feature)
326 bb.debug(1, "Features set %s (was %s)" % (original_featureset, list(self.featureset)))
327 if (original_featureset != list(self.featureset)) and self.state != state.error:
328 self.reset()
329
330 def initConfigurationData(self):
331
332 self.state = state.initial
333 self.caches_array = []
334
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500335 # Need to preserve BB_CONSOLELOG over resets
336 consolelog = None
337 if hasattr(self, "data"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500338 consolelog = self.data.getVar("BB_CONSOLELOG")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500339
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500340 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
341 self.enableDataTracking()
342
343 all_extra_cache_names = []
344 # We hardcode all known cache types in a single place, here.
345 if CookerFeatures.HOB_EXTRA_CACHES in self.featureset:
346 all_extra_cache_names.append("bb.cache_extra:HobRecipeInfo")
347
348 caches_name_array = ['bb.cache:CoreRecipeInfo'] + all_extra_cache_names
349
350 # At least CoreRecipeInfo will be loaded, so caches_array will never be empty!
351 # This is the entry point, no further check needed!
352 for var in caches_name_array:
353 try:
354 module_name, cache_name = var.split(':')
355 module = __import__(module_name, fromlist=(cache_name,))
356 self.caches_array.append(getattr(module, cache_name))
357 except ImportError as exc:
358 logger.critical("Unable to import extra RecipeInfo '%s' from '%s': %s" % (cache_name, module_name, exc))
359 sys.exit("FATAL: Failed to import extra cache class '%s'." % cache_name)
360
361 self.databuilder = bb.cookerdata.CookerDataBuilder(self.configuration, False)
362 self.databuilder.parseBaseConfiguration()
363 self.data = self.databuilder.data
364 self.data_hash = self.databuilder.data_hash
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500365 self.extraconfigdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500366
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500367 if consolelog:
368 self.data.setVar("BB_CONSOLELOG", consolelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500369
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500370 self.data.setVar('BB_CMDLINE', self.ui_cmdline)
371
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500372 #
373 # Copy of the data store which has been expanded.
374 # Used for firing events and accessing variables where expansion needs to be accounted for
375 #
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500376 bb.parse.init_parser(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500377
378 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
379 self.disableDataTracking()
380
381 self.data.renameVar("__depends", "__base_depends")
382 self.add_filewatch(self.data.getVar("__base_depends", False), self.configwatcher)
383
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500384 self.baseconfig_valid = True
385 self.parsecache_valid = False
386
387 def handlePRServ(self):
388 # Setup a PR Server based on the new configuration
389 try:
390 self.prhost = prserv.serv.auto_start(self.data)
391 except prserv.serv.PRServiceConfigError as e:
392 bb.fatal("Unable to start PR Server, exitting")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500393
394 def enableDataTracking(self):
395 self.configuration.tracking = True
396 if hasattr(self, "data"):
397 self.data.enableTracking()
398
399 def disableDataTracking(self):
400 self.configuration.tracking = False
401 if hasattr(self, "data"):
402 self.data.disableTracking()
403
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500404 def parseConfiguration(self):
405 # Set log file verbosity
406 verboselogs = bb.utils.to_boolean(self.data.getVar("BB_VERBOSE_LOGS", False))
407 if verboselogs:
408 bb.msg.loggerVerboseLogs = True
409
410 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500411 nice = self.data.getVar("BB_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500412 if nice:
413 curnice = os.nice(0)
414 nice = int(nice) - curnice
415 buildlog.verbose("Renice to %s " % os.nice(nice))
416
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600417 if self.recipecaches:
418 del self.recipecaches
419 self.multiconfigs = self.databuilder.mcdata.keys()
420 self.recipecaches = {}
421 for mc in self.multiconfigs:
422 self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500423
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500424 self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500425
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500426 self.parsecache_valid = False
427
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500428 def updateConfigOpts(self, options, environment, cmdline):
429 self.ui_cmdline = cmdline
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500430 clean = True
431 for o in options:
432 if o in ['prefile', 'postfile']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500433 # Only these options may require a reparse
434 try:
435 if getattr(self.configuration, o) == options[o]:
436 # Value is the same, no need to mark dirty
437 continue
438 except AttributeError:
439 pass
440 logger.debug(1, "Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
441 print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500442 clean = False
443 setattr(self.configuration, o, options[o])
444 for k in bb.utils.approved_variables():
445 if k in environment and k not in self.configuration.env:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500446 logger.debug(1, "Updating new environment variable %s to %s" % (k, environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500447 self.configuration.env[k] = environment[k]
448 clean = False
449 if k in self.configuration.env and k not in environment:
450 logger.debug(1, "Updating environment variable %s (deleted)" % (k))
451 del self.configuration.env[k]
452 clean = False
453 if k not in self.configuration.env and k not in environment:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500454 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500455 if environment[k] != self.configuration.env[k]:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500456 logger.debug(1, "Updating environment variable %s from %s to %s" % (k, self.configuration.env[k], environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500457 self.configuration.env[k] = environment[k]
458 clean = False
459 if not clean:
460 logger.debug(1, "Base environment change, triggering reparse")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500461 self.reset()
462
463 def runCommands(self, server, data, abort):
464 """
465 Run any queued asynchronous command
466 This is done by the idle handler so it runs in true context rather than
467 tied to any UI.
468 """
469
470 return self.command.runAsyncCommand()
471
472 def showVersions(self):
473
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500474 (latest_versions, preferred_versions) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500475
476 logger.plain("%-35s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version")
477 logger.plain("%-35s %25s %25s\n", "===========", "==============", "=================")
478
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500479 for p in sorted(self.recipecaches[''].pkg_pn):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500480 pref = preferred_versions[p]
481 latest = latest_versions[p]
482
483 prefstr = pref[0][0] + ":" + pref[0][1] + '-' + pref[0][2]
484 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
485
486 if pref == latest:
487 prefstr = ""
488
489 logger.plain("%-35s %25s %25s", p, lateststr, prefstr)
490
491 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
492 """
493 Show the outer or per-recipe environment
494 """
495 fn = None
496 envdata = None
497 if not pkgs_to_build:
498 pkgs_to_build = []
499
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500500 orig_tracking = self.configuration.tracking
501 if not orig_tracking:
502 self.enableDataTracking()
503 self.reset()
504
505
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500506 if buildfile:
507 # Parse the configuration here. We need to do it explicitly here since
508 # this showEnvironment() code path doesn't use the cache
509 self.parseConfiguration()
510
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600511 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500512 fn = self.matchFile(fn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600513 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500514 elif len(pkgs_to_build) == 1:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500515 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500516 if pkgs_to_build[0] in set(ignore.split()):
517 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
518
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600519 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.abort, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500520
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600521 mc = runlist[0][0]
522 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500523 else:
524 envdata = self.data
Brad Bishop316dfdd2018-06-25 12:45:53 -0400525 data.expandKeys(envdata)
526 parse.ast.runAnonFuncs(envdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500527
528 if fn:
529 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600530 bb_cache = bb.cache.Cache(self.databuilder, self.data_hash, self.caches_array)
531 envdata = bb_cache.loadDataFull(fn, self.collection.get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500532 except Exception as e:
533 parselog.exception("Unable to read %s", fn)
534 raise
535
536 # Display history
537 with closing(StringIO()) as env:
538 self.data.inchistory.emit(env)
539 logger.plain(env.getvalue())
540
541 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500542 with closing(StringIO()) as env:
543 data.emit_env(env, envdata, True)
544 logger.plain(env.getvalue())
545
546 # emit the metadata which isnt valid shell
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500547 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600548 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500549 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500550
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500551 if not orig_tracking:
552 self.disableDataTracking()
553 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500554
555 def buildTaskData(self, pkgs_to_build, task, abort, allowincomplete=False):
556 """
557 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
558 """
559 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
560
561 # A task of None means use the default task
562 if task is None:
563 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500564 if not task.startswith("do_"):
565 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500567 targetlist = self.checkPackages(pkgs_to_build, task)
568 fulltargetlist = []
569 defaulttask_implicit = ''
570 defaulttask_explicit = False
571 wildcard = False
572
573 # Wild card expansion:
574 # Replace string such as "multiconfig:*:bash"
575 # into "multiconfig:A:bash multiconfig:B:bash bash"
576 for k in targetlist:
577 if k.startswith("multiconfig:"):
578 if wildcard:
579 bb.fatal('multiconfig conflict')
580 if k.split(":")[1] == "*":
581 wildcard = True
582 for mc in self.multiconfigs:
583 if mc:
584 fulltargetlist.append(k.replace('*', mc))
585 # implicit default task
586 else:
587 defaulttask_implicit = k.split(":")[2]
588 else:
589 fulltargetlist.append(k)
590 else:
591 defaulttask_explicit = True
592 fulltargetlist.append(k)
593
594 if not defaulttask_explicit and defaulttask_implicit != '':
595 fulltargetlist.append(defaulttask_implicit)
596
597 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600598 taskdata = {}
599 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500600
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600601 for mc in self.multiconfigs:
602 taskdata[mc] = bb.taskdata.TaskData(abort, skiplist=self.skiplist, allowincomplete=allowincomplete)
603 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600604 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500605
606 current = 0
607 runlist = []
608 for k in fulltargetlist:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600609 mc = ""
610 if k.startswith("multiconfig:"):
611 mc = k.split(":")[1]
612 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500613 ktask = task
614 if ":do_" in k:
615 k2 = k.split(":do_")
616 k = k2[0]
617 ktask = k2[1]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600618 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500619 current += 1
620 if not ktask.startswith("do_"):
621 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600622 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
623 # e.g. in ASSUME_PROVIDED
624 continue
625 fn = taskdata[mc].build_targets[k][0]
626 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600628
Brad Bishopf058f492019-01-28 23:50:33 -0500629
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800630 # No need to do check providers if there are no mcdeps or not an mc build
Andrew Geissler99467da2019-02-25 18:54:23 -0600631 if len(self.multiconfigs) > 1:
632 seen = set()
633 new = True
634 # Make sure we can provide the multiconfig dependency
635 while new:
636 mcdeps = set()
637 # Add unresolved first, so we can get multiconfig indirect dependencies on time
638 for mc in self.multiconfigs:
639 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
640 mcdeps |= set(taskdata[mc].get_mcdepends())
641 new = False
642 for mc in self.multiconfigs:
643 for k in mcdeps:
644 if k in seen:
645 continue
646 l = k.split(':')
647 depmc = l[2]
648 if depmc not in self.multiconfigs:
649 bb.fatal("Multiconfig dependency %s depends on nonexistent mc configuration %s" % (k,depmc))
650 else:
651 logger.debug(1, "Adding providers for multiconfig dependency %s" % l[3])
652 taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
653 seen.add(k)
654 new = True
Brad Bishopf058f492019-01-28 23:50:33 -0500655
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600656 for mc in self.multiconfigs:
657 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
658
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500659 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600660 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500661
662 def prepareTreeData(self, pkgs_to_build, task):
663 """
664 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
665 """
666
667 # We set abort to False here to prevent unbuildable targets raising
668 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600669 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500670
671 return runlist, taskdata
672
673 ######## WARNING : this function requires cache_extra to be enabled ########
674
675 def generateTaskDepTreeData(self, pkgs_to_build, task):
676 """
677 Create a dependency graph of pkgs_to_build including reverse dependency
678 information.
679 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500680 if not task.startswith("do_"):
681 task = "do_%s" % task
682
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500683 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600684 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500685 rq.rqdata.prepare()
686 return self.buildDependTree(rq, taskdata)
687
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600688 @staticmethod
689 def add_mc_prefix(mc, pn):
690 if mc:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500691 return "multiconfig:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600692 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500693
694 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600695 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500696 depend_tree = {}
697 depend_tree["depends"] = {}
698 depend_tree["tdepends"] = {}
699 depend_tree["pn"] = {}
700 depend_tree["rdepends-pn"] = {}
701 depend_tree["packages"] = {}
702 depend_tree["rdepends-pkg"] = {}
703 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500704 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600705 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500706
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600707 for mc in taskdata:
708 for name, fn in list(taskdata[mc].get_providermap().items()):
709 pn = self.recipecaches[mc].pkg_fn[fn]
710 pn = self.add_mc_prefix(mc, pn)
711 if name != pn:
712 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
713 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500714
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600715 for tid in rq.rqdata.runtaskentries:
716 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
717 pn = self.recipecaches[mc].pkg_fn[taskfn]
718 pn = self.add_mc_prefix(mc, pn)
719 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500720 if pn not in depend_tree["pn"]:
721 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600722 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500723 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600724 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725
726 # if we have extra caches, list all attributes they bring in
727 extra_info = []
728 for cache_class in self.caches_array:
729 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
730 cachefields = getattr(cache_class, 'cachefields', [])
731 extra_info = extra_info + cachefields
732
733 # for all attributes stored, add them to the dependency tree
734 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600735 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736
737
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500738 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
739 if not dotname in depend_tree["tdepends"]:
740 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600741 for dep in rq.rqdata.runtaskentries[tid].depends:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800742 (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
743 deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600744 depend_tree["tdepends"][dotname].append("%s.%s" % (deppn, bb.runqueue.taskname_from_tid(dep)))
745 if taskfn not in seen_fns:
746 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500747 packages = []
748
749 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600750 for dep in taskdata[mc].depids[taskfn]:
751 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500752
753 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600754 for rdep in taskdata[mc].rdepids[taskfn]:
755 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600757 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500758 for package in rdepends:
759 depend_tree["rdepends-pkg"][package] = []
760 for rdepend in rdepends[package]:
761 depend_tree["rdepends-pkg"][package].append(rdepend)
762 packages.append(package)
763
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600764 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500765 for package in rrecs:
766 depend_tree["rrecs-pkg"][package] = []
767 for rdepend in rrecs[package]:
768 depend_tree["rrecs-pkg"][package].append(rdepend)
769 if not package in packages:
770 packages.append(package)
771
772 for package in packages:
773 if package not in depend_tree["packages"]:
774 depend_tree["packages"][package] = {}
775 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600776 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500777 depend_tree["packages"][package]["version"] = version
778
779 return depend_tree
780
781 ######## WARNING : this function requires cache_extra to be enabled ########
782 def generatePkgDepTreeData(self, pkgs_to_build, task):
783 """
784 Create a dependency tree of pkgs_to_build, returning the data.
785 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500786 if not task.startswith("do_"):
787 task = "do_%s" % task
788
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500789 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500790
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600791 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500792 depend_tree = {}
793 depend_tree["depends"] = {}
794 depend_tree["pn"] = {}
795 depend_tree["rdepends-pn"] = {}
796 depend_tree["rdepends-pkg"] = {}
797 depend_tree["rrecs-pkg"] = {}
798
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
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600806 tids = []
807 for mc in taskdata:
808 for tid in taskdata[mc].taskentries:
809 tids.append(tid)
810
811 for tid in tids:
812 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
813
814 pn = self.recipecaches[mc].pkg_fn[taskfn]
815 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500816
817 if pn not in depend_tree["pn"]:
818 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600819 depend_tree["pn"][pn]["filename"] = taskfn
820 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600822 rdepends = self.recipecaches[mc].rundeps[taskfn]
823 rrecs = self.recipecaches[mc].runrecs[taskfn]
824 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500825
826 # for all extra attributes stored, add them to the dependency tree
827 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600828 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500829
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600830 if taskfn not in seen_fns:
831 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500832
833 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500834 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500835 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600836 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
837 fn_provider = taskdata[mc].build_targets[dep][0]
838 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500839 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500840 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600841 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500842 depend_tree["depends"][pn].append(pn_provider)
843
844 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600845 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500846 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600847 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
848 fn_rprovider = taskdata[mc].run_targets[rdep][0]
849 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500850 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600851 pn_rprovider = rdep
852 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500853 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
854
855 depend_tree["rdepends-pkg"].update(rdepends)
856 depend_tree["rrecs-pkg"].update(rrecs)
857
858 return depend_tree
859
860 def generateDepTreeEvent(self, pkgs_to_build, task):
861 """
862 Create a task dependency graph of pkgs_to_build.
863 Generate an event with the result
864 """
865 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
866 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
867
868 def generateDotGraphFiles(self, pkgs_to_build, task):
869 """
870 Create a task dependency graph of pkgs_to_build.
871 Save the result to a set of .dot files.
872 """
873
874 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
875
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500876 with open('pn-buildlist', 'w') as f:
877 for pn in depgraph["pn"]:
878 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500879 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500880
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500881 # Remove old format output files to ensure no confusion with stale data
882 try:
883 os.unlink('pn-depends.dot')
884 except FileNotFoundError:
885 pass
886 try:
887 os.unlink('package-depends.dot')
888 except FileNotFoundError:
889 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500890
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500891 with open('task-depends.dot', 'w') as f:
892 f.write("digraph depends {\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400893 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500894 (pn, taskname) = task.rsplit(".", 1)
895 fn = depgraph["pn"][pn]["filename"]
896 version = depgraph["pn"][pn]["version"]
897 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 -0400898 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500899 f.write('"%s" -> "%s"\n' % (task, dep))
900 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500901 logger.info("Task dependencies saved to 'task-depends.dot'")
902
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500903 with open('recipe-depends.dot', 'w') as f:
904 f.write("digraph depends {\n")
905 pndeps = {}
Brad Bishop316dfdd2018-06-25 12:45:53 -0400906 for task in sorted(depgraph["tdepends"]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500907 (pn, taskname) = task.rsplit(".", 1)
908 if pn not in pndeps:
909 pndeps[pn] = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400910 for dep in sorted(depgraph["tdepends"][task]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500911 (deppn, deptaskname) = dep.rsplit(".", 1)
912 pndeps[pn].add(deppn)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400913 for pn in sorted(pndeps):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500914 fn = depgraph["pn"][pn]["filename"]
915 version = depgraph["pn"][pn]["version"]
916 f.write('"%s" [label="%s\\n%s\\n%s"]\n' % (pn, pn, version, fn))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400917 for dep in sorted(pndeps[pn]):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500918 if dep == pn:
919 continue
920 f.write('"%s" -> "%s"\n' % (pn, dep))
921 f.write("}\n")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400922 logger.info("Flattened recipe dependencies saved to 'recipe-depends.dot'")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500923
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500924 def show_appends_with_no_recipes(self):
925 # Determine which bbappends haven't been applied
926
927 # First get list of recipes, including skipped
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600928 recipefns = list(self.recipecaches[''].pkg_fn.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500929 recipefns.extend(self.skiplist.keys())
930
931 # Work out list of bbappends that have been applied
932 applied_appends = []
933 for fn in recipefns:
934 applied_appends.extend(self.collection.get_file_appends(fn))
935
936 appends_without_recipes = []
937 for _, appendfn in self.collection.bbappends:
938 if not appendfn in applied_appends:
939 appends_without_recipes.append(appendfn)
940
941 if appends_without_recipes:
942 msg = 'No recipes available for:\n %s' % '\n '.join(appends_without_recipes)
943 warn_only = self.data.getVar("BB_DANGLINGAPPENDS_WARNONLY", \
944 False) or "no"
945 if warn_only.lower() in ("1", "yes", "true"):
946 bb.warn(msg)
947 else:
948 bb.fatal(msg)
949
950 def handlePrefProviders(self):
951
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600952 for mc in self.multiconfigs:
953 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600954 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500955
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600956 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500957 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600958 try:
959 (providee, provider) = p.split(':')
960 except:
961 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
962 continue
963 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
964 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
965 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500966
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500967 def findConfigFilePath(self, configfile):
968 """
969 Find the location on disk of configfile and if it exists and was parsed by BitBake
970 emit the ConfigFilePathFound event with the path to the file.
971 """
972 path = bb.cookerdata.findConfigFile(configfile, self.data)
973 if not path:
974 return
975
976 # Generate a list of parsed configuration files by searching the files
977 # listed in the __depends and __base_depends variables with a .conf suffix.
978 conffiles = []
979 dep_files = self.data.getVar('__base_depends', False) or []
980 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
981
982 for f in dep_files:
983 if f[0].endswith(".conf"):
984 conffiles.append(f[0])
985
986 _, conf, conffile = path.rpartition("conf/")
987 match = os.path.join(conf, conffile)
988 # Try and find matches for conf/conffilename.conf as we don't always
989 # have the full path to the file.
990 for cfg in conffiles:
991 if cfg.endswith(match):
992 bb.event.fire(bb.event.ConfigFilePathFound(path),
993 self.data)
994 break
995
996 def findFilesMatchingInDir(self, filepattern, directory):
997 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500998 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500999 'directory' in each BBPATH. i.e. to find all rootfs package classes available
1000 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
1001 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001002 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001003 """
1004
1005 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001006 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001007 for path in bbpaths:
1008 dirpath = os.path.join(path, directory)
1009 if os.path.exists(dirpath):
1010 for root, dirs, files in os.walk(dirpath):
1011 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001012 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001013 matches.append(f)
1014
1015 if matches:
1016 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
1017
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001018 def findProviders(self, mc=''):
1019 return bb.providers.findProviders(self.data, self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
1020
1021 def findBestProvider(self, pn, mc=''):
1022 if pn in self.recipecaches[mc].providers:
1023 filenames = self.recipecaches[mc].providers[pn]
1024 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.data, self.recipecaches[mc])
1025 filename = eligible[0]
1026 return None, None, None, filename
1027 elif pn in self.recipecaches[mc].pkg_pn:
1028 return bb.providers.findBestProvider(pn, self.data, self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
1029 else:
1030 return None, None, None, None
1031
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001032 def findConfigFiles(self, varname):
1033 """
1034 Find config files which are appropriate values for varname.
1035 i.e. MACHINE, DISTRO
1036 """
1037 possible = []
1038 var = varname.lower()
1039
1040 data = self.data
1041 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001042 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001043 for path in bbpaths:
1044 confpath = os.path.join(path, "conf", var)
1045 if os.path.exists(confpath):
1046 for root, dirs, files in os.walk(confpath):
1047 # get all child files, these are appropriate values
1048 for f in files:
1049 val, sep, end = f.rpartition('.')
1050 if end == 'conf':
1051 possible.append(val)
1052
1053 if possible:
1054 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1055
1056 def findInheritsClass(self, klass):
1057 """
1058 Find all recipes which inherit the specified class
1059 """
1060 pkg_list = []
1061
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001062 for pfn in self.recipecaches[''].pkg_fn:
1063 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001064 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001065 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001066
1067 return pkg_list
1068
1069 def generateTargetsTree(self, klass=None, pkgs=None):
1070 """
1071 Generate a dependency tree of buildable targets
1072 Generate an event with the result
1073 """
1074 # if the caller hasn't specified a pkgs list default to universe
1075 if not pkgs:
1076 pkgs = ['universe']
1077 # if inherited_class passed ensure all recipes which inherit the
1078 # specified class are included in pkgs
1079 if klass:
1080 extra_pkgs = self.findInheritsClass(klass)
1081 pkgs = pkgs + extra_pkgs
1082
1083 # generate a dependency tree for all our packages
1084 tree = self.generatePkgDepTreeData(pkgs, 'build')
1085 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1086
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001087 def interactiveMode( self ):
1088 """Drop off into a shell"""
1089 try:
1090 from bb import shell
1091 except ImportError:
1092 parselog.exception("Interactive mode not available")
1093 sys.exit(1)
1094 else:
1095 shell.start( self )
1096
1097
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001098 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001099 """Handle collections"""
1100 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001101 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001102 if collections:
1103 collection_priorities = {}
1104 collection_depends = {}
1105 collection_list = collections.split()
1106 min_prio = 0
1107 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001108 bb.debug(1,'Processing %s in collection list' % (c))
1109
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001110 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001111 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001112 if priority:
1113 try:
1114 prio = int(priority)
1115 except ValueError:
1116 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1117 errors = True
1118 if min_prio == 0 or prio < min_prio:
1119 min_prio = prio
1120 collection_priorities[c] = prio
1121 else:
1122 collection_priorities[c] = None
1123
1124 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001125 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001126 if deps:
1127 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001128 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001129 except bb.utils.VersionStringException as vse:
1130 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001131 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001132 if dep in collection_list:
1133 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001134 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001135 (op, depver) = opstr.split()
1136 if layerver:
1137 try:
1138 res = bb.utils.vercmp_string_op(layerver, depver, op)
1139 except bb.utils.VersionStringException as vse:
1140 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1141 if not res:
1142 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)
1143 errors = True
1144 else:
1145 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)
1146 errors = True
1147 else:
1148 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1149 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001150 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001151 else:
1152 collection_depends[c] = []
1153
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001154 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001155 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001156 if recs:
1157 try:
1158 recDict = bb.utils.explode_dep_versions2(recs)
1159 except bb.utils.VersionStringException as vse:
1160 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1161 for rec, oplist in list(recDict.items()):
1162 if rec in collection_list:
1163 if oplist:
1164 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001165 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001166 if layerver:
1167 (op, recver) = opstr.split()
1168 try:
1169 res = bb.utils.vercmp_string_op(layerver, recver, op)
1170 except bb.utils.VersionStringException as vse:
1171 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1172 if not res:
1173 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)
1174 continue
1175 else:
1176 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)
1177 continue
1178 parselog.debug(3,"Layer '%s' recommends layer '%s', so we are adding it", c, rec)
1179 collection_depends[c].append(rec)
1180 else:
1181 parselog.debug(3,"Layer '%s' recommends layer '%s', but this layer is not enabled in your configuration", c, rec)
1182
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001183 # Recursively work out collection priorities based on dependencies
1184 def calc_layer_priority(collection):
1185 if not collection_priorities[collection]:
1186 max_depprio = min_prio
1187 for dep in collection_depends[collection]:
1188 calc_layer_priority(dep)
1189 depprio = collection_priorities[dep]
1190 if depprio > max_depprio:
1191 max_depprio = depprio
1192 max_depprio += 1
1193 parselog.debug(1, "Calculated priority of layer %s as %d", collection, max_depprio)
1194 collection_priorities[collection] = max_depprio
1195
1196 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1197 for c in collection_list:
1198 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001199 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001200 if regex == None:
1201 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1202 errors = True
1203 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001204 elif regex == "":
1205 parselog.debug(1, "BBFILE_PATTERN_%s is empty" % c)
Brad Bishop19323692019-04-05 15:28:33 -04001206 cre = re.compile('^NULL$')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001207 errors = False
1208 else:
1209 try:
1210 cre = re.compile(regex)
1211 except re.error:
1212 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1213 errors = True
1214 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001215 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001216 if errors:
1217 # We've already printed the actual error(s)
1218 raise CollectionError("Errors during parsing layer configuration")
1219
1220 def buildSetVars(self):
1221 """
1222 Setup any variables needed before starting a build
1223 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001224 t = time.gmtime()
1225 for mc in self.databuilder.mcdata:
1226 ds = self.databuilder.mcdata[mc]
1227 if not ds.getVar("BUILDNAME", False):
1228 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1229 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1230 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1231 ds.setVar("TIME", time.strftime('%H%M%S', t))
1232
1233 def reset_mtime_caches(self):
1234 """
1235 Reset mtime caches - this is particularly important when memory resident as something
1236 which is cached is not unlikely to have changed since the last invocation (e.g. a
1237 file associated with a recipe might have been modified by the user).
1238 """
1239 build.reset_cache()
1240 bb.fetch._checksum_cache.mtime_cache.clear()
1241 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1242 if siggen_cache:
1243 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001244
1245 def matchFiles(self, bf):
1246 """
1247 Find the .bb files which match the expression in 'buildfile'.
1248 """
1249 if bf.startswith("/") or bf.startswith("../"):
1250 bf = os.path.abspath(bf)
1251
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001252 self.collection = CookerCollectFiles(self.bbfile_config_priorities)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001253 filelist, masked, searchdirs = self.collection.collect_bbfiles(self.data, self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001254 try:
1255 os.stat(bf)
1256 bf = os.path.abspath(bf)
1257 return [bf]
1258 except OSError:
1259 regexp = re.compile(bf)
1260 matches = []
1261 for f in filelist:
1262 if regexp.search(f) and os.path.isfile(f):
1263 matches.append(f)
1264 return matches
1265
1266 def matchFile(self, buildfile):
1267 """
1268 Find the .bb file which matches the expression in 'buildfile'.
1269 Raise an error if multiple files
1270 """
1271 matches = self.matchFiles(buildfile)
1272 if len(matches) != 1:
1273 if matches:
1274 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1275 if matches:
1276 for f in matches:
1277 msg += "\n %s" % f
1278 parselog.error(msg)
1279 else:
1280 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1281 raise NoSpecificMatch
1282 return matches[0]
1283
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001284 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001285 """
1286 Build the file matching regexp buildfile
1287 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001288 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001289
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001290 # Too many people use -b because they think it's how you normally
1291 # specify a target to be built, so show a warning
1292 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1293
1294 self.buildFileInternal(buildfile, task)
1295
1296 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1297 """
1298 Build the file matching regexp buildfile
1299 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001300
1301 # Parse the configuration here. We need to do it explicitly here since
1302 # buildFile() doesn't use the cache
1303 self.parseConfiguration()
1304
1305 # If we are told to do the None task then query the default task
1306 if (task == None):
1307 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001308 if not task.startswith("do_"):
1309 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001310
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001311 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001312 fn = self.matchFile(fn)
1313
1314 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001315 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001316
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001317 bb_cache = bb.cache.Cache(self.databuilder, self.data_hash, self.caches_array)
1318
1319 infos = bb_cache.parse(fn, self.collection.get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001320 infos = dict(infos)
1321
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001322 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001323 try:
1324 info_array = infos[fn]
1325 except KeyError:
1326 bb.fatal("%s does not exist" % fn)
1327
1328 if info_array[0].skipped:
1329 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1330
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001331 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001332
1333 # Tweak some variables
1334 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001335 self.recipecaches[mc].ignored_dependencies = set()
1336 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001337 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001338
1339 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001340 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1341 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001342 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1343 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001344
1345 # Invalidate task for target if force mode active
1346 if self.configuration.force:
1347 logger.verbose("Invalidate task %s, %s", task, fn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001348 bb.parse.siggen.invalidate_task(task, self.recipecaches[mc], fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001349
1350 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001351 taskdata = {}
1352 taskdata[mc] = bb.taskdata.TaskData(self.configuration.abort)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001353 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001354
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001355 if quietlog:
1356 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1357 bb.runqueue.logger.setLevel(logging.WARNING)
1358
1359 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1360 if fireevents:
1361 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001362
1363 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001364 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001365
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001366 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001367
1368 def buildFileIdle(server, rq, abort):
1369
1370 msg = None
1371 interrupted = 0
1372 if abort or self.state == state.forceshutdown:
1373 rq.finish_runqueue(True)
1374 msg = "Forced shutdown"
1375 interrupted = 2
1376 elif self.state == state.shutdown:
1377 rq.finish_runqueue(False)
1378 msg = "Stopped build"
1379 interrupted = 1
1380 failures = 0
1381 try:
1382 retval = rq.execute_runqueue()
1383 except runqueue.TaskFailure as exc:
1384 failures += len(exc.args)
1385 retval = False
1386 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001387 self.command.finishAsyncCommand(str(exc))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001388 if quietlog:
1389 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001390 return False
1391
1392 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001393 if fireevents:
1394 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 -05001395 self.command.finishAsyncCommand(msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001396 # We trashed self.recipecaches above
1397 self.parsecache_valid = False
1398 self.configuration.limited_deps = False
1399 bb.parse.siggen.reset(self.data)
1400 if quietlog:
1401 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001402 return False
1403 if retval is True:
1404 return True
1405 return retval
1406
1407 self.configuration.server_register_idlecallback(buildFileIdle, rq)
1408
1409 def buildTargets(self, targets, task):
1410 """
1411 Attempt to build the targets specified
1412 """
1413
1414 def buildTargetsIdle(server, rq, abort):
1415 msg = None
1416 interrupted = 0
1417 if abort or self.state == state.forceshutdown:
1418 rq.finish_runqueue(True)
1419 msg = "Forced shutdown"
1420 interrupted = 2
1421 elif self.state == state.shutdown:
1422 rq.finish_runqueue(False)
1423 msg = "Stopped build"
1424 interrupted = 1
1425 failures = 0
1426 try:
1427 retval = rq.execute_runqueue()
1428 except runqueue.TaskFailure as exc:
1429 failures += len(exc.args)
1430 retval = False
1431 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001432 self.command.finishAsyncCommand(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001433 return False
1434
1435 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001436 try:
1437 for mc in self.multiconfigs:
1438 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1439 finally:
1440 self.command.finishAsyncCommand(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001441 return False
1442 if retval is True:
1443 return True
1444 return retval
1445
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001446 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001447 self.buildSetVars()
1448
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001449 # If we are told to do the None task then query the default task
1450 if (task == None):
1451 task = self.configuration.cmd
1452
1453 if not task.startswith("do_"):
1454 task = "do_%s" % task
1455
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001456 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1457
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001458 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001459
1460 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.abort)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001461
1462 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001463
1464 # make targets to always look as <target>:do_<task>
1465 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001466 for target in runlist:
1467 if target[0]:
1468 ntargets.append("multiconfig:%s:%s:%s" % (target[0], target[1], target[2]))
1469 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001470
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001471 for mc in self.multiconfigs:
1472 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001473
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001474 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001475 if 'universe' in targets:
1476 rq.rqdata.warn_multi_bb = True
1477
1478 self.configuration.server_register_idlecallback(buildTargetsIdle, rq)
1479
1480
1481 def getAllKeysWithFlags(self, flaglist):
1482 dump = {}
1483 for k in self.data.keys():
1484 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001485 expand = True
1486 flags = self.data.getVarFlags(k)
1487 if flags and "func" in flags and "python" in flags:
1488 expand = False
1489 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001490 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1491 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001492 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001493 'history' : self.data.varhistory.variable(k),
1494 }
1495 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001496 if flags and d in flags:
1497 dump[k][d] = flags[d]
1498 else:
1499 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001500 except Exception as e:
1501 print(e)
1502 return dump
1503
1504
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001505 def updateCacheSync(self):
1506 if self.state == state.running:
1507 return
1508
1509 # reload files for which we got notifications
1510 for p in self.inotify_modified_files:
1511 bb.parse.update_cache(p)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001512 if p in bb.parse.BBHandler.cached_statements:
1513 del bb.parse.BBHandler.cached_statements[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001514 self.inotify_modified_files = []
1515
1516 if not self.baseconfig_valid:
1517 logger.debug(1, "Reloading base configuration data")
1518 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001519 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001520
1521 # This is called for all async commands when self.state != running
1522 def updateCache(self):
1523 if self.state == state.running:
1524 return
1525
1526 if self.state in (state.shutdown, state.forceshutdown, state.error):
1527 if hasattr(self.parser, 'shutdown'):
1528 self.parser.shutdown(clean=False, force = True)
1529 raise bb.BBHandledException()
1530
1531 if self.state != state.parsing:
1532 self.updateCacheSync()
1533
1534 if self.state != state.parsing and not self.parsecache_valid:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001535 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001536 self.parseConfiguration ()
1537 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001538 for mc in self.multiconfigs:
1539 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001540
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001541 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001542 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001543 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001544
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001545 for dep in self.configuration.extra_assume_provided:
1546 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001547
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001548 self.collection = CookerCollectFiles(self.bbfile_config_priorities)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001549 (filelist, masked, searchdirs) = self.collection.collect_bbfiles(self.data, self.data)
1550
1551 # Add inotify watches for directories searched for bb/bbappend files
1552 for dirent in searchdirs:
1553 self.add_filewatch([[dirent]], dirs=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001554
1555 self.parser = CookerParser(self, filelist, masked)
1556 self.parsecache_valid = True
1557
1558 self.state = state.parsing
1559
1560 if not self.parser.parse_next():
1561 collectlog.debug(1, "parsing complete")
1562 if self.parser.error:
1563 raise bb.BBHandledException()
1564 self.show_appends_with_no_recipes()
1565 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001566 for mc in self.multiconfigs:
1567 self.recipecaches[mc].bbfile_priority = self.collection.collection_priorities(self.recipecaches[mc].pkg_fn, self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001568 self.state = state.running
1569
1570 # Send an event listing all stamps reachable after parsing
1571 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001572 for mc in self.multiconfigs:
1573 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1574 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001575 return None
1576
1577 return True
1578
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001579 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001580
1581 # Return a copy, don't modify the original
1582 pkgs_to_build = pkgs_to_build[:]
1583
1584 if len(pkgs_to_build) == 0:
1585 raise NothingToBuild
1586
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001587 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001588 for pkg in pkgs_to_build:
1589 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001590 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001591
1592 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001593 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001594 for mc in self.multiconfigs:
1595 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1596 for t in self.recipecaches[mc].world_target:
1597 if mc:
1598 t = "multiconfig:" + mc + ":" + t
1599 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001600
1601 if 'universe' in pkgs_to_build:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001602 parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001603 parselog.debug(1, "collating packages for \"universe\"")
1604 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001605 for mc in self.multiconfigs:
1606 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001607 if task:
1608 foundtask = False
1609 for provider_fn in self.recipecaches[mc].providers[t]:
1610 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1611 foundtask = True
1612 break
1613 if not foundtask:
1614 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1615 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001616 if mc:
1617 t = "multiconfig:" + mc + ":" + t
1618 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001619
1620 return pkgs_to_build
1621
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001622 def pre_serve(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001623 # We now are in our own process so we can call this here.
1624 # PRServ exits if its parent process exits
1625 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001626 return
1627
1628 def post_serve(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001629 prserv.serv.auto_shutdown()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001630 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001631
1632
1633 def shutdown(self, force = False):
1634 if force:
1635 self.state = state.forceshutdown
1636 else:
1637 self.state = state.shutdown
1638
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001639 if self.parser:
1640 self.parser.shutdown(clean=not force, force=force)
1641
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001642 def finishcommand(self):
1643 self.state = state.initial
1644
1645 def reset(self):
1646 self.initConfigurationData()
1647
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001648 def clientComplete(self):
1649 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001650 self.finishcommand()
1651 self.extraconfigdata = {}
1652 self.command.reset()
1653 self.databuilder.reset()
1654 self.data = self.databuilder.data
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001655
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001656
1657class CookerExit(bb.event.Event):
1658 """
1659 Notify clients of the Cooker shutdown
1660 """
1661
1662 def __init__(self):
1663 bb.event.Event.__init__(self)
1664
1665
1666class CookerCollectFiles(object):
1667 def __init__(self, priorities):
1668 self.bbappends = []
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001669 # Priorities is a list of tupples, with the second element as the pattern.
1670 # We need to sort the list with the longest pattern first, and so on to
1671 # the shortest. This allows nested layers to be properly evaluated.
1672 self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001673
1674 def calc_bbfile_priority( self, filename, matched = None ):
1675 for _, _, regex, pri in self.bbfile_config_priorities:
1676 if regex.match(filename):
1677 if matched != None:
1678 if not regex in matched:
1679 matched.add(regex)
1680 return pri
1681 return 0
1682
1683 def get_bbfiles(self):
1684 """Get list of default .bb files by reading out the current directory"""
1685 path = os.getcwd()
1686 contents = os.listdir(path)
1687 bbfiles = []
1688 for f in contents:
1689 if f.endswith(".bb"):
1690 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1691 return bbfiles
1692
1693 def find_bbfiles(self, path):
1694 """Find all the .bb and .bbappend files in a directory"""
1695 found = []
1696 for dir, dirs, files in os.walk(path):
1697 for ignored in ('SCCS', 'CVS', '.svn'):
1698 if ignored in dirs:
1699 dirs.remove(ignored)
1700 found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
1701
1702 return found
1703
1704 def collect_bbfiles(self, config, eventdata):
1705 """Collect all available .bb build files"""
1706 masked = 0
1707
1708 collectlog.debug(1, "collecting .bb files")
1709
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001710 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001711 config.setVar("BBFILES", " ".join(files))
1712
1713 # Sort files by priority
1714 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem) )
1715
1716 if not len(files):
1717 files = self.get_bbfiles()
1718
1719 if not len(files):
1720 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1721 bb.event.fire(CookerExit(), eventdata)
1722
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001723 # We need to track where we look so that we can add inotify watches. There
1724 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001725 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001726 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001727 if hasattr(os, 'scandir'):
1728 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001729 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001730
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001731 def ourlistdir(d):
1732 searchdirs.append(d)
1733 return origlistdir(d)
1734
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001735 def ourscandir(d):
1736 searchdirs.append(d)
1737 return origscandir(d)
1738
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001739 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001740 if hasattr(os, 'scandir'):
1741 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001742 try:
1743 # Can't use set here as order is important
1744 newfiles = []
1745 for f in files:
1746 if os.path.isdir(f):
1747 dirfiles = self.find_bbfiles(f)
1748 for g in dirfiles:
1749 if g not in newfiles:
1750 newfiles.append(g)
1751 else:
1752 globbed = glob.glob(f)
1753 if not globbed and os.path.exists(f):
1754 globbed = [f]
1755 # glob gives files in order on disk. Sort to be deterministic.
1756 for g in sorted(globbed):
1757 if g not in newfiles:
1758 newfiles.append(g)
1759 finally:
1760 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001761 if hasattr(os, 'scandir'):
1762 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001763
1764 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001765
1766 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001767 # First validate the individual regular expressions and ignore any
1768 # that do not compile
1769 bbmasks = []
1770 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001771 # When constructing an older style single regex, it's possible for BBMASK
1772 # to end up beginning with '|', which matches and masks _everything_.
1773 if mask.startswith("|"):
1774 collectlog.warn("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
1775 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001776 try:
1777 re.compile(mask)
1778 bbmasks.append(mask)
1779 except sre_constants.error:
1780 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1781
1782 # Then validate the combined regular expressions. This should never
1783 # fail, but better safe than sorry...
1784 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001785 try:
1786 bbmask_compiled = re.compile(bbmask)
1787 except sre_constants.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001788 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1789 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001790
1791 bbfiles = []
1792 bbappend = []
1793 for f in newfiles:
1794 if bbmask and bbmask_compiled.search(f):
1795 collectlog.debug(1, "skipping masked file %s", f)
1796 masked += 1
1797 continue
1798 if f.endswith('.bb'):
1799 bbfiles.append(f)
1800 elif f.endswith('.bbappend'):
1801 bbappend.append(f)
1802 else:
1803 collectlog.debug(1, "skipping %s: unknown file extension", f)
1804
1805 # Build a list of .bbappend files for each .bb file
1806 for f in bbappend:
1807 base = os.path.basename(f).replace('.bbappend', '.bb')
1808 self.bbappends.append((base, f))
1809
1810 # Find overlayed recipes
1811 # bbfiles will be in priority order which makes this easy
1812 bbfile_seen = dict()
1813 self.overlayed = defaultdict(list)
1814 for f in reversed(bbfiles):
1815 base = os.path.basename(f)
1816 if base not in bbfile_seen:
1817 bbfile_seen[base] = f
1818 else:
1819 topfile = bbfile_seen[base]
1820 self.overlayed[topfile].append(f)
1821
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001822 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001823
1824 def get_file_appends(self, fn):
1825 """
1826 Returns a list of .bbappend files to apply to fn
1827 """
1828 filelist = []
1829 f = os.path.basename(fn)
1830 for b in self.bbappends:
1831 (bbappend, filename) = b
1832 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1833 filelist.append(filename)
1834 return filelist
1835
1836 def collection_priorities(self, pkgfns, d):
1837
1838 priorities = {}
1839
1840 # Calculate priorities for each file
1841 matched = set()
1842 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001843 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001844 priorities[p] = self.calc_bbfile_priority(realfn, matched)
1845
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001846 unmatched = set()
1847 for _, _, regex, pri in self.bbfile_config_priorities:
1848 if not regex in matched:
1849 unmatched.add(regex)
1850
Brad Bishop316dfdd2018-06-25 12:45:53 -04001851 # Don't show the warning if the BBFILE_PATTERN did match .bbappend files
1852 def find_bbappend_match(regex):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001853 for b in self.bbappends:
1854 (bbfile, append) = b
1855 if regex.match(append):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001856 # If the bbappend is matched by already "matched set", return False
1857 for matched_regex in matched:
1858 if matched_regex.match(append):
1859 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001860 return True
1861 return False
1862
1863 for unmatch in unmatched.copy():
Brad Bishop316dfdd2018-06-25 12:45:53 -04001864 if find_bbappend_match(unmatch):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001865 unmatched.remove(unmatch)
1866
1867 for collection, pattern, regex, _ in self.bbfile_config_priorities:
1868 if regex in unmatched:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001869 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001870 collectlog.warning("No bb files matched BBFILE_PATTERN_%s '%s'" % (collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001871
1872 return priorities
1873
1874class ParsingFailure(Exception):
1875 def __init__(self, realexception, recipe):
1876 self.realexception = realexception
1877 self.recipe = recipe
1878 Exception.__init__(self, realexception, recipe)
1879
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001880class Parser(multiprocessing.Process):
1881 def __init__(self, jobs, results, quit, init, profile):
1882 self.jobs = jobs
1883 self.results = results
1884 self.quit = quit
1885 self.init = init
1886 multiprocessing.Process.__init__(self)
1887 self.context = bb.utils.get_context().copy()
1888 self.handlers = bb.event.get_class_handlers().copy()
1889 self.profile = profile
1890
1891 def run(self):
1892
1893 if not self.profile:
1894 self.realrun()
1895 return
1896
1897 try:
1898 import cProfile as profile
1899 except:
1900 import profile
1901 prof = profile.Profile()
1902 try:
1903 profile.Profile.runcall(prof, self.realrun)
1904 finally:
1905 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
1906 prof.dump_stats(logfile)
1907
1908 def realrun(self):
1909 if self.init:
1910 self.init()
1911
1912 pending = []
1913 while True:
1914 try:
1915 self.quit.get_nowait()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001916 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001917 pass
1918 else:
1919 self.results.cancel_join_thread()
1920 break
1921
1922 if pending:
1923 result = pending.pop()
1924 else:
1925 try:
Brad Bishop19323692019-04-05 15:28:33 -04001926 job = self.jobs.pop()
1927 except IndexError:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001928 break
1929 result = self.parse(*job)
1930
1931 try:
1932 self.results.put(result, timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001933 except queue.Full:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001934 pending.append(result)
1935
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001936 def parse(self, filename, appends):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001937 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001938 # Record the filename we're parsing into any events generated
1939 def parse_filter(self, record):
1940 record.taskpid = bb.event.worker_pid
1941 record.fn = filename
1942 return True
1943
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001944 # Reset our environment and handlers to the original settings
1945 bb.utils.set_context(self.context.copy())
1946 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001947 bb.event.LogHandler.filter = parse_filter
1948
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001949 return True, self.bb_cache.parse(filename, appends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001950 except Exception as exc:
1951 tb = sys.exc_info()[2]
1952 exc.recipe = filename
1953 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
1954 return True, exc
1955 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
1956 # and for example a worker thread doesn't just exit on its own in response to
1957 # a SystemExit event for example.
1958 except BaseException as exc:
1959 return True, ParsingFailure(exc, filename)
1960
1961class CookerParser(object):
1962 def __init__(self, cooker, filelist, masked):
1963 self.filelist = filelist
1964 self.cooker = cooker
1965 self.cfgdata = cooker.data
1966 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001967 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001968
1969 # Accounting statistics
1970 self.parsed = 0
1971 self.cached = 0
1972 self.error = 0
1973 self.masked = masked
1974
1975 self.skipped = 0
1976 self.virtuals = 0
1977 self.total = len(filelist)
1978
1979 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001980 self.process_names = []
1981
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001982 self.bb_cache = bb.cache.Cache(self.cfgbuilder, self.cfghash, cooker.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001983 self.fromcache = []
1984 self.willparse = []
1985 for filename in self.filelist:
1986 appends = self.cooker.collection.get_file_appends(filename)
1987 if not self.bb_cache.cacheValid(filename, appends):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001988 self.willparse.append((filename, appends))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001989 else:
1990 self.fromcache.append((filename, appends))
1991 self.toparse = self.total - len(self.fromcache)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001992 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001993
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001994 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001995 multiprocessing.cpu_count()), len(self.willparse))
1996
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001997 self.start()
1998 self.haveshutdown = False
1999
2000 def start(self):
2001 self.results = self.load_cached()
2002 self.processes = []
2003 if self.toparse:
2004 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
2005 def init():
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002006 Parser.bb_cache = self.bb_cache
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002007 bb.utils.set_process_name(multiprocessing.current_process().name)
2008 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2009 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002010
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002011 self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002012 self.result_queue = multiprocessing.Queue()
Brad Bishop19323692019-04-05 15:28:33 -04002013
2014 def chunkify(lst,n):
2015 return [lst[i::n] for i in range(n)]
2016 self.jobs = chunkify(self.willparse, self.num_processes)
2017
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002018 for i in range(0, self.num_processes):
Brad Bishop19323692019-04-05 15:28:33 -04002019 parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, init, self.cooker.configuration.profile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002020 parser.start()
2021 self.process_names.append(parser.name)
2022 self.processes.append(parser)
2023
2024 self.results = itertools.chain(self.results, self.parse_generator())
2025
2026 def shutdown(self, clean=True, force=False):
2027 if not self.toparse:
2028 return
2029 if self.haveshutdown:
2030 return
2031 self.haveshutdown = True
2032
2033 if clean:
2034 event = bb.event.ParseCompleted(self.cached, self.parsed,
2035 self.skipped, self.masked,
2036 self.virtuals, self.error,
2037 self.total)
2038
2039 bb.event.fire(event, self.cfgdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002040 for process in self.processes:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002041 self.parser_quit.put(None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002042 else:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002043 self.parser_quit.cancel_join_thread()
2044 for process in self.processes:
2045 self.parser_quit.put(None)
2046
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002047 for process in self.processes:
2048 if force:
2049 process.join(.1)
2050 process.terminate()
2051 else:
2052 process.join()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002053
2054 sync = threading.Thread(target=self.bb_cache.sync)
2055 sync.start()
2056 multiprocessing.util.Finalize(None, sync.join, exitpriority=-100)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002057 bb.codeparser.parser_cache_savemerge()
2058 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002059 if self.cooker.configuration.profile:
2060 profiles = []
2061 for i in self.process_names:
2062 logfile = "profile-parse-%s.log" % i
2063 if os.path.exists(logfile):
2064 profiles.append(logfile)
2065
2066 pout = "profile-parse.log.processed"
2067 bb.utils.process_profilelog(profiles, pout = pout)
2068 print("Processed parsing statistics saved to %s" % (pout))
2069
2070 def load_cached(self):
2071 for filename, appends in self.fromcache:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002072 cached, infos = self.bb_cache.load(filename, appends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002073 yield not cached, infos
2074
2075 def parse_generator(self):
2076 while True:
2077 if self.parsed >= self.toparse:
2078 break
2079
2080 try:
2081 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002082 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002083 pass
2084 else:
2085 value = result[1]
2086 if isinstance(value, BaseException):
2087 raise value
2088 else:
2089 yield result
2090
2091 def parse_next(self):
2092 result = []
2093 parsed = None
2094 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002095 parsed, result = next(self.results)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002096 except StopIteration:
2097 self.shutdown()
2098 return False
2099 except bb.BBHandledException as exc:
2100 self.error += 1
2101 logger.error('Failed to parse recipe: %s' % exc.recipe)
2102 self.shutdown(clean=False)
2103 return False
2104 except ParsingFailure as exc:
2105 self.error += 1
2106 logger.error('Unable to parse %s: %s' %
2107 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
2108 self.shutdown(clean=False)
2109 return False
2110 except bb.parse.ParseError as exc:
2111 self.error += 1
2112 logger.error(str(exc))
2113 self.shutdown(clean=False)
2114 return False
2115 except bb.data_smart.ExpansionError as exc:
2116 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002117 bbdir = os.path.dirname(__file__) + os.sep
2118 etype, value, _ = sys.exc_info()
2119 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2120 logger.error('ExpansionError during parsing %s', value.recipe,
2121 exc_info=(etype, value, tb))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002122 self.shutdown(clean=False)
2123 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002124 except Exception as exc:
2125 self.error += 1
2126 etype, value, tb = sys.exc_info()
2127 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002128 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002129 exc_info=(etype, value, exc.traceback))
2130 else:
2131 # Most likely, an exception occurred during raising an exception
2132 import traceback
2133 logger.error('Exception during parse: %s' % traceback.format_exc())
2134 self.shutdown(clean=False)
2135 return False
2136
2137 self.current += 1
2138 self.virtuals += len(result)
2139 if parsed:
2140 self.parsed += 1
2141 if self.parsed % self.progress_chunk == 0:
2142 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2143 self.cfgdata)
2144 else:
2145 self.cached += 1
2146
2147 for virtualfn, info_array in result:
2148 if info_array[0].skipped:
2149 self.skipped += 1
2150 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002151 (fn, cls, mc) = bb.cache.virtualfn2realfn(virtualfn)
2152 self.bb_cache.add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002153 parsed=parsed, watcher = self.cooker.add_filewatch)
2154 return True
2155
2156 def reparse(self, filename):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002157 infos = self.bb_cache.parse(filename, self.cooker.collection.get_file_appends(filename))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002158 for vfn, info_array in infos:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002159 (fn, cls, mc) = bb.cache.virtualfn2realfn(vfn)
2160 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)