blob: c7fdd72901f4ab99cc145e06e22115dacaebe79c [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#!/usr/bin/env python
2# ex:ts=4:sw=4:sts=4:et
3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4#
5# Copyright (C) 2003, 2004 Chris Larson
6# Copyright (C) 2003, 2004 Phil Blundell
7# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
8# Copyright (C) 2005 Holger Hans Peter Freyther
9# Copyright (C) 2005 ROAD GmbH
10# Copyright (C) 2006 - 2007 Richard Purdie
11#
12# This program is free software; you can redistribute it and/or modify
13# it under the terms of the GNU General Public License version 2 as
14# published by the Free Software Foundation.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License along
22# with this program; if not, write to the Free Software Foundation, Inc.,
23# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24
Patrick Williamsc0f7c042017-02-23 20:41:17 -060025
Patrick Williamsc124f4f2015-09-15 14:41:29 -050026import sys, os, glob, os.path, re, time
27import atexit
28import itertools
29import logging
30import multiprocessing
31import sre_constants
32import threading
Patrick Williamsc0f7c042017-02-23 20:41:17 -060033from io import StringIO, UnsupportedOperation
Patrick Williamsc124f4f2015-09-15 14:41:29 -050034from contextlib import closing
35from functools import wraps
Patrick Williamsc0f7c042017-02-23 20:41:17 -060036from collections import defaultdict, namedtuple
Patrick Williamsc124f4f2015-09-15 14:41:29 -050037import bb, bb.exceptions, bb.command
38from bb import utils, data, parse, event, cache, providers, taskdata, runqueue, build
Patrick Williamsc0f7c042017-02-23 20:41:17 -060039import queue
Patrick Williamsc124f4f2015-09-15 14:41:29 -050040import signal
41import subprocess
42import errno
43import prserv.serv
44import pyinotify
Patrick Williamsc0f7c042017-02-23 20:41:17 -060045import json
46import pickle
47import codecs
Patrick Williamsc124f4f2015-09-15 14:41:29 -050048
49logger = logging.getLogger("BitBake")
50collectlog = logging.getLogger("BitBake.Collection")
51buildlog = logging.getLogger("BitBake.Build")
52parselog = logging.getLogger("BitBake.Parsing")
53providerlog = logging.getLogger("BitBake.Provider")
54
55class NoSpecificMatch(bb.BBHandledException):
56 """
57 Exception raised when no or multiple file matches are found
58 """
59
60class NothingToBuild(Exception):
61 """
62 Exception raised when there is nothing to build
63 """
64
65class CollectionError(bb.BBHandledException):
66 """
67 Exception raised when layer configuration is incorrect
68 """
69
70class state:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060071 initial, parsing, running, shutdown, forceshutdown, stopped, error = list(range(7))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050072
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050073 @classmethod
74 def get_name(cls, code):
75 for name in dir(cls):
76 value = getattr(cls, name)
77 if type(value) == type(cls.initial) and value == code:
78 return name
79 raise ValueError("Invalid status code: %s" % code)
80
Patrick Williamsc124f4f2015-09-15 14:41:29 -050081
82class SkippedPackage:
83 def __init__(self, info = None, reason = None):
84 self.pn = None
85 self.skipreason = None
86 self.provides = None
87 self.rprovides = None
88
89 if info:
90 self.pn = info.pn
91 self.skipreason = info.skipreason
92 self.provides = info.provides
93 self.rprovides = info.rprovides
94 elif reason:
95 self.skipreason = reason
96
97
98class CookerFeatures(object):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060099 _feature_list = [HOB_EXTRA_CACHES, BASEDATASTORE_TRACKING, SEND_SANITYEVENTS] = list(range(3))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500100
101 def __init__(self):
102 self._features=set()
103
104 def setFeature(self, f):
105 # validate we got a request for a feature we support
106 if f not in CookerFeatures._feature_list:
107 return
108 self._features.add(f)
109
110 def __contains__(self, f):
111 return f in self._features
112
113 def __iter__(self):
114 return self._features.__iter__()
115
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600116 def __next__(self):
117 return next(self._features)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500118
119
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600120class EventWriter:
121 def __init__(self, cooker, eventfile):
122 self.file_inited = None
123 self.cooker = cooker
124 self.eventfile = eventfile
125 self.event_queue = []
126
127 def write_event(self, event):
128 with open(self.eventfile, "a") as f:
129 try:
130 str_event = codecs.encode(pickle.dumps(event), 'base64').decode('utf-8')
131 f.write("%s\n" % json.dumps({"class": event.__module__ + "." + event.__class__.__name__,
132 "vars": str_event}))
133 except Exception as err:
134 import traceback
135 print(err, traceback.format_exc())
136
137 def send(self, event):
138 if self.file_inited:
139 # we have the file, just write the event
140 self.write_event(event)
141 else:
142 # init on bb.event.BuildStarted
143 name = "%s.%s" % (event.__module__, event.__class__.__name__)
144 if name in ("bb.event.BuildStarted", "bb.cooker.CookerExit"):
145 with open(self.eventfile, "w") as f:
146 f.write("%s\n" % json.dumps({ "allvariables" : self.cooker.getAllKeysWithFlags(["doc", "func"])}))
147
148 self.file_inited = True
149
150 # write pending events
151 for evt in self.event_queue:
152 self.write_event(evt)
153
154 # also write the current event
155 self.write_event(event)
156 else:
157 # queue all events until the file is inited
158 self.event_queue.append(event)
159
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500160#============================================================================#
161# BBCooker
162#============================================================================#
163class BBCooker:
164 """
165 Manages one bitbake build run
166 """
167
168 def __init__(self, configuration, featureSet=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600169 self.recipecaches = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500170 self.skiplist = {}
171 self.featureset = CookerFeatures()
172 if featureSet:
173 for f in featureSet:
174 self.featureset.setFeature(f)
175
176 self.configuration = configuration
177
178 self.configwatcher = pyinotify.WatchManager()
179 self.configwatcher.bbseen = []
180 self.configwatcher.bbwatchedfiles = []
181 self.confignotifier = pyinotify.Notifier(self.configwatcher, self.config_notifications)
182 self.watchmask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CREATE | pyinotify.IN_DELETE | \
183 pyinotify.IN_DELETE_SELF | pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF | \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500184 pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500185 self.watcher = pyinotify.WatchManager()
186 self.watcher.bbseen = []
187 self.watcher.bbwatchedfiles = []
188 self.notifier = pyinotify.Notifier(self.watcher, self.notifications)
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
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600199 # we log all events to a file if so directed
200 if self.configuration.writeeventlog:
201 # register the log file writer as UI Handler
202 writer = EventWriter(self, self.configuration.writeeventlog)
203 EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
204 bb.event.register_UIHhandler(EventLogWriteHandler(writer))
205
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500206 self.inotify_modified_files = []
207
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500208 def _process_inotify_updates(server, cooker, abort):
209 cooker.process_inotify_updates()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500210 return 1.0
211
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500212 self.configuration.server_register_idlecallback(_process_inotify_updates, self)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500213
214 # TOSTOP must not be set or our children will hang when they output
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600215 try:
216 fd = sys.stdout.fileno()
217 if os.isatty(fd):
218 import termios
219 tcattr = termios.tcgetattr(fd)
220 if tcattr[3] & termios.TOSTOP:
221 buildlog.info("The terminal had the TOSTOP bit set, clearing...")
222 tcattr[3] = tcattr[3] & ~termios.TOSTOP
223 termios.tcsetattr(fd, termios.TCSANOW, tcattr)
224 except UnsupportedOperation:
225 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500226
227 self.command = bb.command.Command(self)
228 self.state = state.initial
229
230 self.parser = None
231
232 signal.signal(signal.SIGTERM, self.sigterm_exception)
233 # Let SIGHUP exit as SIGTERM
234 signal.signal(signal.SIGHUP, self.sigterm_exception)
235
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500236 def process_inotify_updates(self):
237 for n in [self.confignotifier, self.notifier]:
238 if n.check_events(timeout=0):
239 # read notified events and enqeue them
240 n.read_events()
241 n.process_events()
242
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500243 def config_notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500244 if event.maskname == "IN_Q_OVERFLOW":
245 bb.warn("inotify event queue overflowed, invalidating caches.")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500246 self.parsecache_valid = False
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500247 self.baseconfig_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500248 bb.parse.clear_cache()
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500249 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500250 if not event.pathname in self.configwatcher.bbwatchedfiles:
251 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500252 if not event.pathname in self.inotify_modified_files:
253 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500254 self.baseconfig_valid = False
255
256 def notifications(self, event):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500257 if event.maskname == "IN_Q_OVERFLOW":
258 bb.warn("inotify event queue overflowed, invalidating caches.")
259 self.parsecache_valid = False
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500260 bb.parse.clear_cache()
261 return
262 if event.pathname.endswith("bitbake-cookerdaemon.log") \
263 or event.pathname.endswith("bitbake.lock"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500264 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500265 if not event.pathname in self.inotify_modified_files:
266 self.inotify_modified_files.append(event.pathname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500267 self.parsecache_valid = False
268
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500269 def add_filewatch(self, deps, watcher=None, dirs=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500270 if not watcher:
271 watcher = self.watcher
272 for i in deps:
273 watcher.bbwatchedfiles.append(i[0])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500274 if dirs:
275 f = i[0]
276 else:
277 f = os.path.dirname(i[0])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500278 if f in watcher.bbseen:
279 continue
280 watcher.bbseen.append(f)
281 watchtarget = None
282 while True:
283 # We try and add watches for files that don't exist but if they did, would influence
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500284 # the parser. The parent directory of these files may not exist, in which case we need
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500285 # to watch any parent that does exist for changes.
286 try:
287 watcher.add_watch(f, self.watchmask, quiet=False)
288 if watchtarget:
289 watcher.bbwatchedfiles.append(watchtarget)
290 break
291 except pyinotify.WatchManagerError as e:
292 if 'ENOENT' in str(e):
293 watchtarget = f
294 f = os.path.dirname(f)
295 if f in watcher.bbseen:
296 break
297 watcher.bbseen.append(f)
298 continue
299 if 'ENOSPC' in str(e):
300 providerlog.error("No space left on device or exceeds fs.inotify.max_user_watches?")
301 providerlog.error("To check max_user_watches: sysctl -n fs.inotify.max_user_watches.")
302 providerlog.error("To modify max_user_watches: sysctl -n -w fs.inotify.max_user_watches=<value>.")
303 providerlog.error("Root privilege is required to modify max_user_watches.")
304 raise
305
306 def sigterm_exception(self, signum, stackframe):
307 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500308 bb.warn("Cooker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500309 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500310 bb.warn("Cooker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311 self.state = state.forceshutdown
312
313 def setFeatures(self, features):
314 # we only accept a new feature set if we're in state initial, so we can reset without problems
315 if not self.state in [state.initial, state.shutdown, state.forceshutdown, state.stopped, state.error]:
316 raise Exception("Illegal state for feature set change")
317 original_featureset = list(self.featureset)
318 for feature in features:
319 self.featureset.setFeature(feature)
320 bb.debug(1, "Features set %s (was %s)" % (original_featureset, list(self.featureset)))
321 if (original_featureset != list(self.featureset)) and self.state != state.error:
322 self.reset()
323
324 def initConfigurationData(self):
325
326 self.state = state.initial
327 self.caches_array = []
328
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500329 # Need to preserve BB_CONSOLELOG over resets
330 consolelog = None
331 if hasattr(self, "data"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500332 consolelog = self.data.getVar("BB_CONSOLELOG")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500333
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500334 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
335 self.enableDataTracking()
336
337 all_extra_cache_names = []
338 # We hardcode all known cache types in a single place, here.
339 if CookerFeatures.HOB_EXTRA_CACHES in self.featureset:
340 all_extra_cache_names.append("bb.cache_extra:HobRecipeInfo")
341
342 caches_name_array = ['bb.cache:CoreRecipeInfo'] + all_extra_cache_names
343
344 # At least CoreRecipeInfo will be loaded, so caches_array will never be empty!
345 # This is the entry point, no further check needed!
346 for var in caches_name_array:
347 try:
348 module_name, cache_name = var.split(':')
349 module = __import__(module_name, fromlist=(cache_name,))
350 self.caches_array.append(getattr(module, cache_name))
351 except ImportError as exc:
352 logger.critical("Unable to import extra RecipeInfo '%s' from '%s': %s" % (cache_name, module_name, exc))
353 sys.exit("FATAL: Failed to import extra cache class '%s'." % cache_name)
354
355 self.databuilder = bb.cookerdata.CookerDataBuilder(self.configuration, False)
356 self.databuilder.parseBaseConfiguration()
357 self.data = self.databuilder.data
358 self.data_hash = self.databuilder.data_hash
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500359 self.extraconfigdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500360
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500361 if consolelog:
362 self.data.setVar("BB_CONSOLELOG", consolelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500363
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500364 self.data.setVar('BB_CMDLINE', self.ui_cmdline)
365
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500366 #
367 # Copy of the data store which has been expanded.
368 # Used for firing events and accessing variables where expansion needs to be accounted for
369 #
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500370 bb.parse.init_parser(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500371
372 if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
373 self.disableDataTracking()
374
375 self.data.renameVar("__depends", "__base_depends")
376 self.add_filewatch(self.data.getVar("__base_depends", False), self.configwatcher)
377
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500378 self.baseconfig_valid = True
379 self.parsecache_valid = False
380
381 def handlePRServ(self):
382 # Setup a PR Server based on the new configuration
383 try:
384 self.prhost = prserv.serv.auto_start(self.data)
385 except prserv.serv.PRServiceConfigError as e:
386 bb.fatal("Unable to start PR Server, exitting")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500387
388 def enableDataTracking(self):
389 self.configuration.tracking = True
390 if hasattr(self, "data"):
391 self.data.enableTracking()
392
393 def disableDataTracking(self):
394 self.configuration.tracking = False
395 if hasattr(self, "data"):
396 self.data.disableTracking()
397
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500398 def parseConfiguration(self):
399 # Set log file verbosity
400 verboselogs = bb.utils.to_boolean(self.data.getVar("BB_VERBOSE_LOGS", False))
401 if verboselogs:
402 bb.msg.loggerVerboseLogs = True
403
404 # Change nice level if we're asked to
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500405 nice = self.data.getVar("BB_NICE_LEVEL")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500406 if nice:
407 curnice = os.nice(0)
408 nice = int(nice) - curnice
409 buildlog.verbose("Renice to %s " % os.nice(nice))
410
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600411 if self.recipecaches:
412 del self.recipecaches
413 self.multiconfigs = self.databuilder.mcdata.keys()
414 self.recipecaches = {}
415 for mc in self.multiconfigs:
416 self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500417
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500418 self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500419
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500420 self.parsecache_valid = False
421
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500422 def updateConfigOpts(self, options, environment, cmdline):
423 self.ui_cmdline = cmdline
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500424 clean = True
425 for o in options:
426 if o in ['prefile', 'postfile']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500427 # Only these options may require a reparse
428 try:
429 if getattr(self.configuration, o) == options[o]:
430 # Value is the same, no need to mark dirty
431 continue
432 except AttributeError:
433 pass
434 logger.debug(1, "Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
435 print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500436 clean = False
437 setattr(self.configuration, o, options[o])
438 for k in bb.utils.approved_variables():
439 if k in environment and k not in self.configuration.env:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500440 logger.debug(1, "Updating new environment variable %s to %s" % (k, environment[k]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500441 self.configuration.env[k] = environment[k]
442 clean = False
443 if k in self.configuration.env and k not in environment:
444 logger.debug(1, "Updating environment variable %s (deleted)" % (k))
445 del self.configuration.env[k]
446 clean = False
447 if k not in self.configuration.env and k not in environment:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500448 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500449 if environment[k] != self.configuration.env[k]:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500450 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 -0500451 self.configuration.env[k] = environment[k]
452 clean = False
453 if not clean:
454 logger.debug(1, "Base environment change, triggering reparse")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500455 self.reset()
456
457 def runCommands(self, server, data, abort):
458 """
459 Run any queued asynchronous command
460 This is done by the idle handler so it runs in true context rather than
461 tied to any UI.
462 """
463
464 return self.command.runAsyncCommand()
465
466 def showVersions(self):
467
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500468 (latest_versions, preferred_versions) = self.findProviders()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500469
470 logger.plain("%-35s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version")
471 logger.plain("%-35s %25s %25s\n", "===========", "==============", "=================")
472
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500473 for p in sorted(self.recipecaches[''].pkg_pn):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500474 pref = preferred_versions[p]
475 latest = latest_versions[p]
476
477 prefstr = pref[0][0] + ":" + pref[0][1] + '-' + pref[0][2]
478 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
479
480 if pref == latest:
481 prefstr = ""
482
483 logger.plain("%-35s %25s %25s", p, lateststr, prefstr)
484
485 def showEnvironment(self, buildfile=None, pkgs_to_build=None):
486 """
487 Show the outer or per-recipe environment
488 """
489 fn = None
490 envdata = None
491 if not pkgs_to_build:
492 pkgs_to_build = []
493
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500494 orig_tracking = self.configuration.tracking
495 if not orig_tracking:
496 self.enableDataTracking()
497 self.reset()
498
499
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500500 if buildfile:
501 # Parse the configuration here. We need to do it explicitly here since
502 # this showEnvironment() code path doesn't use the cache
503 self.parseConfiguration()
504
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600505 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500506 fn = self.matchFile(fn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600507 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500508 elif len(pkgs_to_build) == 1:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500509 ignore = self.data.getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500510 if pkgs_to_build[0] in set(ignore.split()):
511 bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
512
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600513 taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.abort, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500514
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600515 mc = runlist[0][0]
516 fn = runlist[0][3]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500517 else:
518 envdata = self.data
519
520 if fn:
521 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600522 bb_cache = bb.cache.Cache(self.databuilder, self.data_hash, self.caches_array)
523 envdata = bb_cache.loadDataFull(fn, self.collection.get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500524 except Exception as e:
525 parselog.exception("Unable to read %s", fn)
526 raise
527
528 # Display history
529 with closing(StringIO()) as env:
530 self.data.inchistory.emit(env)
531 logger.plain(env.getvalue())
532
533 # emit variables and shell functions
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534 with closing(StringIO()) as env:
535 data.emit_env(env, envdata, True)
536 logger.plain(env.getvalue())
537
538 # emit the metadata which isnt valid shell
539 data.expandKeys(envdata)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500540 for e in sorted(envdata.keys()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600541 if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500542 logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500543
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500544 if not orig_tracking:
545 self.disableDataTracking()
546 self.reset()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500547
548 def buildTaskData(self, pkgs_to_build, task, abort, allowincomplete=False):
549 """
550 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
551 """
552 bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
553
554 # A task of None means use the default task
555 if task is None:
556 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500557 if not task.startswith("do_"):
558 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500559
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500560 targetlist = self.checkPackages(pkgs_to_build, task)
561 fulltargetlist = []
562 defaulttask_implicit = ''
563 defaulttask_explicit = False
564 wildcard = False
565
566 # Wild card expansion:
567 # Replace string such as "multiconfig:*:bash"
568 # into "multiconfig:A:bash multiconfig:B:bash bash"
569 for k in targetlist:
570 if k.startswith("multiconfig:"):
571 if wildcard:
572 bb.fatal('multiconfig conflict')
573 if k.split(":")[1] == "*":
574 wildcard = True
575 for mc in self.multiconfigs:
576 if mc:
577 fulltargetlist.append(k.replace('*', mc))
578 # implicit default task
579 else:
580 defaulttask_implicit = k.split(":")[2]
581 else:
582 fulltargetlist.append(k)
583 else:
584 defaulttask_explicit = True
585 fulltargetlist.append(k)
586
587 if not defaulttask_explicit and defaulttask_implicit != '':
588 fulltargetlist.append(defaulttask_implicit)
589
590 bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600591 taskdata = {}
592 localdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500593
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600594 for mc in self.multiconfigs:
595 taskdata[mc] = bb.taskdata.TaskData(abort, skiplist=self.skiplist, allowincomplete=allowincomplete)
596 localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600597 bb.data.expandKeys(localdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500598
599 current = 0
600 runlist = []
601 for k in fulltargetlist:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600602 mc = ""
603 if k.startswith("multiconfig:"):
604 mc = k.split(":")[1]
605 k = ":".join(k.split(":")[2:])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500606 ktask = task
607 if ":do_" in k:
608 k2 = k.split(":do_")
609 k = k2[0]
610 ktask = k2[1]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600611 taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500612 current += 1
613 if not ktask.startswith("do_"):
614 ktask = "do_%s" % ktask
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600615 if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
616 # e.g. in ASSUME_PROVIDED
617 continue
618 fn = taskdata[mc].build_targets[k][0]
619 runlist.append([mc, k, ktask, fn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500620 bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600621
622 for mc in self.multiconfigs:
623 taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
624
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500625 bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600626 return taskdata, runlist
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627
628 def prepareTreeData(self, pkgs_to_build, task):
629 """
630 Prepare a runqueue and taskdata object for iteration over pkgs_to_build
631 """
632
633 # We set abort to False here to prevent unbuildable targets raising
634 # an exception when we're just generating data
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600635 taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500636
637 return runlist, taskdata
638
639 ######## WARNING : this function requires cache_extra to be enabled ########
640
641 def generateTaskDepTreeData(self, pkgs_to_build, task):
642 """
643 Create a dependency graph of pkgs_to_build including reverse dependency
644 information.
645 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500646 if not task.startswith("do_"):
647 task = "do_%s" % task
648
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500649 runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600650 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 rq.rqdata.prepare()
652 return self.buildDependTree(rq, taskdata)
653
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600654 @staticmethod
655 def add_mc_prefix(mc, pn):
656 if mc:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500657 return "multiconfig:%s:%s" % (mc, pn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600658 return pn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500659
660 def buildDependTree(self, rq, taskdata):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600661 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500662 depend_tree = {}
663 depend_tree["depends"] = {}
664 depend_tree["tdepends"] = {}
665 depend_tree["pn"] = {}
666 depend_tree["rdepends-pn"] = {}
667 depend_tree["packages"] = {}
668 depend_tree["rdepends-pkg"] = {}
669 depend_tree["rrecs-pkg"] = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500670 depend_tree['providermap'] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600671 depend_tree["layer-priorities"] = self.bbfile_config_priorities
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500672
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600673 for mc in taskdata:
674 for name, fn in list(taskdata[mc].get_providermap().items()):
675 pn = self.recipecaches[mc].pkg_fn[fn]
676 pn = self.add_mc_prefix(mc, pn)
677 if name != pn:
678 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
679 depend_tree['providermap'][name] = (pn, version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500680
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600681 for tid in rq.rqdata.runtaskentries:
682 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
683 pn = self.recipecaches[mc].pkg_fn[taskfn]
684 pn = self.add_mc_prefix(mc, pn)
685 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500686 if pn not in depend_tree["pn"]:
687 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600688 depend_tree["pn"][pn]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600690 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500691
692 # if we have extra caches, list all attributes they bring in
693 extra_info = []
694 for cache_class in self.caches_array:
695 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
696 cachefields = getattr(cache_class, 'cachefields', [])
697 extra_info = extra_info + cachefields
698
699 # for all attributes stored, add them to the dependency tree
700 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600701 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500702
703
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500704 dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
705 if not dotname in depend_tree["tdepends"]:
706 depend_tree["tdepends"][dotname] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600707 for dep in rq.rqdata.runtaskentries[tid].depends:
708 (depmc, depfn, deptaskname, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
709 deppn = self.recipecaches[mc].pkg_fn[deptaskfn]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600710 depend_tree["tdepends"][dotname].append("%s.%s" % (deppn, bb.runqueue.taskname_from_tid(dep)))
711 if taskfn not in seen_fns:
712 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500713 packages = []
714
715 depend_tree["depends"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600716 for dep in taskdata[mc].depids[taskfn]:
717 depend_tree["depends"][pn].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500718
719 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600720 for rdep in taskdata[mc].rdepids[taskfn]:
721 depend_tree["rdepends-pn"][pn].append(rdep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500722
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 rdepends = self.recipecaches[mc].rundeps[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500724 for package in rdepends:
725 depend_tree["rdepends-pkg"][package] = []
726 for rdepend in rdepends[package]:
727 depend_tree["rdepends-pkg"][package].append(rdepend)
728 packages.append(package)
729
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600730 rrecs = self.recipecaches[mc].runrecs[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731 for package in rrecs:
732 depend_tree["rrecs-pkg"][package] = []
733 for rdepend in rrecs[package]:
734 depend_tree["rrecs-pkg"][package].append(rdepend)
735 if not package in packages:
736 packages.append(package)
737
738 for package in packages:
739 if package not in depend_tree["packages"]:
740 depend_tree["packages"][package] = {}
741 depend_tree["packages"][package]["pn"] = pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600742 depend_tree["packages"][package]["filename"] = taskfn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500743 depend_tree["packages"][package]["version"] = version
744
745 return depend_tree
746
747 ######## WARNING : this function requires cache_extra to be enabled ########
748 def generatePkgDepTreeData(self, pkgs_to_build, task):
749 """
750 Create a dependency tree of pkgs_to_build, returning the data.
751 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500752 if not task.startswith("do_"):
753 task = "do_%s" % task
754
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500755 _, taskdata = self.prepareTreeData(pkgs_to_build, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600757 seen_fns = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500758 depend_tree = {}
759 depend_tree["depends"] = {}
760 depend_tree["pn"] = {}
761 depend_tree["rdepends-pn"] = {}
762 depend_tree["rdepends-pkg"] = {}
763 depend_tree["rrecs-pkg"] = {}
764
765 # if we have extra caches, list all attributes they bring in
766 extra_info = []
767 for cache_class in self.caches_array:
768 if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
769 cachefields = getattr(cache_class, 'cachefields', [])
770 extra_info = extra_info + cachefields
771
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600772 tids = []
773 for mc in taskdata:
774 for tid in taskdata[mc].taskentries:
775 tids.append(tid)
776
777 for tid in tids:
778 (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
779
780 pn = self.recipecaches[mc].pkg_fn[taskfn]
781 pn = self.add_mc_prefix(mc, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500782
783 if pn not in depend_tree["pn"]:
784 depend_tree["pn"][pn] = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600785 depend_tree["pn"][pn]["filename"] = taskfn
786 version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787 depend_tree["pn"][pn]["version"] = version
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600788 rdepends = self.recipecaches[mc].rundeps[taskfn]
789 rrecs = self.recipecaches[mc].runrecs[taskfn]
790 depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791
792 # for all extra attributes stored, add them to the dependency tree
793 for ei in extra_info:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600794 depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500795
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600796 if taskfn not in seen_fns:
797 seen_fns.append(taskfn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500798
799 depend_tree["depends"][pn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500800 for dep in taskdata[mc].depids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500801 pn_provider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600802 if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
803 fn_provider = taskdata[mc].build_targets[dep][0]
804 pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500805 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500806 pn_provider = dep
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600807 pn_provider = self.add_mc_prefix(mc, pn_provider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500808 depend_tree["depends"][pn].append(pn_provider)
809
810 depend_tree["rdepends-pn"][pn] = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600811 for rdep in taskdata[mc].rdepids[taskfn]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500812 pn_rprovider = ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600813 if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
814 fn_rprovider = taskdata[mc].run_targets[rdep][0]
815 pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500816 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600817 pn_rprovider = rdep
818 pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500819 depend_tree["rdepends-pn"][pn].append(pn_rprovider)
820
821 depend_tree["rdepends-pkg"].update(rdepends)
822 depend_tree["rrecs-pkg"].update(rrecs)
823
824 return depend_tree
825
826 def generateDepTreeEvent(self, pkgs_to_build, task):
827 """
828 Create a task dependency graph of pkgs_to_build.
829 Generate an event with the result
830 """
831 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
832 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
833
834 def generateDotGraphFiles(self, pkgs_to_build, task):
835 """
836 Create a task dependency graph of pkgs_to_build.
837 Save the result to a set of .dot files.
838 """
839
840 depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
841
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500842 with open('pn-buildlist', 'w') as f:
843 for pn in depgraph["pn"]:
844 f.write(pn + "\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500845 logger.info("PN build list saved to 'pn-buildlist'")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500846
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500847 # Remove old format output files to ensure no confusion with stale data
848 try:
849 os.unlink('pn-depends.dot')
850 except FileNotFoundError:
851 pass
852 try:
853 os.unlink('package-depends.dot')
854 except FileNotFoundError:
855 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500856
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500857 with open('task-depends.dot', 'w') as f:
858 f.write("digraph depends {\n")
859 for task in depgraph["tdepends"]:
860 (pn, taskname) = task.rsplit(".", 1)
861 fn = depgraph["pn"][pn]["filename"]
862 version = depgraph["pn"][pn]["version"]
863 f.write('"%s.%s" [label="%s %s\\n%s\\n%s"]\n' % (pn, taskname, pn, taskname, version, fn))
864 for dep in depgraph["tdepends"][task]:
865 f.write('"%s" -> "%s"\n' % (task, dep))
866 f.write("}\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500867 logger.info("Task dependencies saved to 'task-depends.dot'")
868
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500869 with open('recipe-depends.dot', 'w') as f:
870 f.write("digraph depends {\n")
871 pndeps = {}
872 for task in depgraph["tdepends"]:
873 (pn, taskname) = task.rsplit(".", 1)
874 if pn not in pndeps:
875 pndeps[pn] = set()
876 for dep in depgraph["tdepends"][task]:
877 (deppn, deptaskname) = dep.rsplit(".", 1)
878 pndeps[pn].add(deppn)
879 for pn in pndeps:
880 fn = depgraph["pn"][pn]["filename"]
881 version = depgraph["pn"][pn]["version"]
882 f.write('"%s" [label="%s\\n%s\\n%s"]\n' % (pn, pn, version, fn))
883 for dep in pndeps[pn]:
884 if dep == pn:
885 continue
886 f.write('"%s" -> "%s"\n' % (pn, dep))
887 f.write("}\n")
888 logger.info("Flatened recipe dependencies saved to 'recipe-depends.dot'")
889
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500890 def show_appends_with_no_recipes(self):
891 # Determine which bbappends haven't been applied
892
893 # First get list of recipes, including skipped
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600894 recipefns = list(self.recipecaches[''].pkg_fn.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500895 recipefns.extend(self.skiplist.keys())
896
897 # Work out list of bbappends that have been applied
898 applied_appends = []
899 for fn in recipefns:
900 applied_appends.extend(self.collection.get_file_appends(fn))
901
902 appends_without_recipes = []
903 for _, appendfn in self.collection.bbappends:
904 if not appendfn in applied_appends:
905 appends_without_recipes.append(appendfn)
906
907 if appends_without_recipes:
908 msg = 'No recipes available for:\n %s' % '\n '.join(appends_without_recipes)
909 warn_only = self.data.getVar("BB_DANGLINGAPPENDS_WARNONLY", \
910 False) or "no"
911 if warn_only.lower() in ("1", "yes", "true"):
912 bb.warn(msg)
913 else:
914 bb.fatal(msg)
915
916 def handlePrefProviders(self):
917
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600918 for mc in self.multiconfigs:
919 localdata = data.createCopy(self.databuilder.mcdata[mc])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600920 bb.data.expandKeys(localdata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500921
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600922 # Handle PREFERRED_PROVIDERS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500923 for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600924 try:
925 (providee, provider) = p.split(':')
926 except:
927 providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
928 continue
929 if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
930 providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
931 self.recipecaches[mc].preferred[providee] = provider
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500932
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500933 def findConfigFilePath(self, configfile):
934 """
935 Find the location on disk of configfile and if it exists and was parsed by BitBake
936 emit the ConfigFilePathFound event with the path to the file.
937 """
938 path = bb.cookerdata.findConfigFile(configfile, self.data)
939 if not path:
940 return
941
942 # Generate a list of parsed configuration files by searching the files
943 # listed in the __depends and __base_depends variables with a .conf suffix.
944 conffiles = []
945 dep_files = self.data.getVar('__base_depends', False) or []
946 dep_files = dep_files + (self.data.getVar('__depends', False) or [])
947
948 for f in dep_files:
949 if f[0].endswith(".conf"):
950 conffiles.append(f[0])
951
952 _, conf, conffile = path.rpartition("conf/")
953 match = os.path.join(conf, conffile)
954 # Try and find matches for conf/conffilename.conf as we don't always
955 # have the full path to the file.
956 for cfg in conffiles:
957 if cfg.endswith(match):
958 bb.event.fire(bb.event.ConfigFilePathFound(path),
959 self.data)
960 break
961
962 def findFilesMatchingInDir(self, filepattern, directory):
963 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500964 Searches for files containing the substring 'filepattern' which are children of
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500965 'directory' in each BBPATH. i.e. to find all rootfs package classes available
966 to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
967 or to find all machine configuration files one could call:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500968 findFilesMatchingInDir(self, '.conf', 'conf/machine')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500969 """
970
971 matches = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500972 bbpaths = self.data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500973 for path in bbpaths:
974 dirpath = os.path.join(path, directory)
975 if os.path.exists(dirpath):
976 for root, dirs, files in os.walk(dirpath):
977 for f in files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500978 if filepattern in f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500979 matches.append(f)
980
981 if matches:
982 bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
983
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500984 def findProviders(self, mc=''):
985 return bb.providers.findProviders(self.data, self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
986
987 def findBestProvider(self, pn, mc=''):
988 if pn in self.recipecaches[mc].providers:
989 filenames = self.recipecaches[mc].providers[pn]
990 eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.data, self.recipecaches[mc])
991 filename = eligible[0]
992 return None, None, None, filename
993 elif pn in self.recipecaches[mc].pkg_pn:
994 return bb.providers.findBestProvider(pn, self.data, self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
995 else:
996 return None, None, None, None
997
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500998 def findConfigFiles(self, varname):
999 """
1000 Find config files which are appropriate values for varname.
1001 i.e. MACHINE, DISTRO
1002 """
1003 possible = []
1004 var = varname.lower()
1005
1006 data = self.data
1007 # iterate configs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001008 bbpaths = data.getVar('BBPATH').split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001009 for path in bbpaths:
1010 confpath = os.path.join(path, "conf", var)
1011 if os.path.exists(confpath):
1012 for root, dirs, files in os.walk(confpath):
1013 # get all child files, these are appropriate values
1014 for f in files:
1015 val, sep, end = f.rpartition('.')
1016 if end == 'conf':
1017 possible.append(val)
1018
1019 if possible:
1020 bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
1021
1022 def findInheritsClass(self, klass):
1023 """
1024 Find all recipes which inherit the specified class
1025 """
1026 pkg_list = []
1027
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001028 for pfn in self.recipecaches[''].pkg_fn:
1029 inherits = self.recipecaches[''].inherits.get(pfn, None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001030 if inherits and klass in inherits:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001031 pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001032
1033 return pkg_list
1034
1035 def generateTargetsTree(self, klass=None, pkgs=None):
1036 """
1037 Generate a dependency tree of buildable targets
1038 Generate an event with the result
1039 """
1040 # if the caller hasn't specified a pkgs list default to universe
1041 if not pkgs:
1042 pkgs = ['universe']
1043 # if inherited_class passed ensure all recipes which inherit the
1044 # specified class are included in pkgs
1045 if klass:
1046 extra_pkgs = self.findInheritsClass(klass)
1047 pkgs = pkgs + extra_pkgs
1048
1049 # generate a dependency tree for all our packages
1050 tree = self.generatePkgDepTreeData(pkgs, 'build')
1051 bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
1052
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001053 def interactiveMode( self ):
1054 """Drop off into a shell"""
1055 try:
1056 from bb import shell
1057 except ImportError:
1058 parselog.exception("Interactive mode not available")
1059 sys.exit(1)
1060 else:
1061 shell.start( self )
1062
1063
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001064 def handleCollections(self, collections):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001065 """Handle collections"""
1066 errors = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001067 self.bbfile_config_priorities = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001068 if collections:
1069 collection_priorities = {}
1070 collection_depends = {}
1071 collection_list = collections.split()
1072 min_prio = 0
1073 for c in collection_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001074 bb.debug(1,'Processing %s in collection list' % (c))
1075
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001076 # Get collection priority if defined explicitly
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001077 priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001078 if priority:
1079 try:
1080 prio = int(priority)
1081 except ValueError:
1082 parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
1083 errors = True
1084 if min_prio == 0 or prio < min_prio:
1085 min_prio = prio
1086 collection_priorities[c] = prio
1087 else:
1088 collection_priorities[c] = None
1089
1090 # Check dependencies and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001091 deps = self.data.getVar("LAYERDEPENDS_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001092 if deps:
1093 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001094 depDict = bb.utils.explode_dep_versions2(deps)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001095 except bb.utils.VersionStringException as vse:
1096 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001097 for dep, oplist in list(depDict.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001098 if dep in collection_list:
1099 for opstr in oplist:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001100 layerver = self.data.getVar("LAYERVERSION_%s" % dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001101 (op, depver) = opstr.split()
1102 if layerver:
1103 try:
1104 res = bb.utils.vercmp_string_op(layerver, depver, op)
1105 except bb.utils.VersionStringException as vse:
1106 bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
1107 if not res:
1108 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)
1109 errors = True
1110 else:
1111 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)
1112 errors = True
1113 else:
1114 parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
1115 errors = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001116 collection_depends[c] = list(depDict.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001117 else:
1118 collection_depends[c] = []
1119
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001120 # Check recommends and store information for priority calculation
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001121 recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001122 if recs:
1123 try:
1124 recDict = bb.utils.explode_dep_versions2(recs)
1125 except bb.utils.VersionStringException as vse:
1126 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1127 for rec, oplist in list(recDict.items()):
1128 if rec in collection_list:
1129 if oplist:
1130 opstr = oplist[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001131 layerver = self.data.getVar("LAYERVERSION_%s" % rec)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001132 if layerver:
1133 (op, recver) = opstr.split()
1134 try:
1135 res = bb.utils.vercmp_string_op(layerver, recver, op)
1136 except bb.utils.VersionStringException as vse:
1137 bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
1138 if not res:
1139 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)
1140 continue
1141 else:
1142 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)
1143 continue
1144 parselog.debug(3,"Layer '%s' recommends layer '%s', so we are adding it", c, rec)
1145 collection_depends[c].append(rec)
1146 else:
1147 parselog.debug(3,"Layer '%s' recommends layer '%s', but this layer is not enabled in your configuration", c, rec)
1148
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001149 # Recursively work out collection priorities based on dependencies
1150 def calc_layer_priority(collection):
1151 if not collection_priorities[collection]:
1152 max_depprio = min_prio
1153 for dep in collection_depends[collection]:
1154 calc_layer_priority(dep)
1155 depprio = collection_priorities[dep]
1156 if depprio > max_depprio:
1157 max_depprio = depprio
1158 max_depprio += 1
1159 parselog.debug(1, "Calculated priority of layer %s as %d", collection, max_depprio)
1160 collection_priorities[collection] = max_depprio
1161
1162 # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
1163 for c in collection_list:
1164 calc_layer_priority(c)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001165 regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001166 if regex == None:
1167 parselog.error("BBFILE_PATTERN_%s not defined" % c)
1168 errors = True
1169 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001170 elif regex == "":
1171 parselog.debug(1, "BBFILE_PATTERN_%s is empty" % c)
1172 errors = False
1173 else:
1174 try:
1175 cre = re.compile(regex)
1176 except re.error:
1177 parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
1178 errors = True
1179 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001180 self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001181 if errors:
1182 # We've already printed the actual error(s)
1183 raise CollectionError("Errors during parsing layer configuration")
1184
1185 def buildSetVars(self):
1186 """
1187 Setup any variables needed before starting a build
1188 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001189 t = time.gmtime()
1190 for mc in self.databuilder.mcdata:
1191 ds = self.databuilder.mcdata[mc]
1192 if not ds.getVar("BUILDNAME", False):
1193 ds.setVar("BUILDNAME", "${DATE}${TIME}")
1194 ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
1195 ds.setVar("DATE", time.strftime('%Y%m%d', t))
1196 ds.setVar("TIME", time.strftime('%H%M%S', t))
1197
1198 def reset_mtime_caches(self):
1199 """
1200 Reset mtime caches - this is particularly important when memory resident as something
1201 which is cached is not unlikely to have changed since the last invocation (e.g. a
1202 file associated with a recipe might have been modified by the user).
1203 """
1204 build.reset_cache()
1205 bb.fetch._checksum_cache.mtime_cache.clear()
1206 siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
1207 if siggen_cache:
1208 bb.parse.siggen.checksum_cache.mtime_cache.clear()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001209
1210 def matchFiles(self, bf):
1211 """
1212 Find the .bb files which match the expression in 'buildfile'.
1213 """
1214 if bf.startswith("/") or bf.startswith("../"):
1215 bf = os.path.abspath(bf)
1216
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001217 self.collection = CookerCollectFiles(self.bbfile_config_priorities)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001218 filelist, masked, searchdirs = self.collection.collect_bbfiles(self.data, self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001219 try:
1220 os.stat(bf)
1221 bf = os.path.abspath(bf)
1222 return [bf]
1223 except OSError:
1224 regexp = re.compile(bf)
1225 matches = []
1226 for f in filelist:
1227 if regexp.search(f) and os.path.isfile(f):
1228 matches.append(f)
1229 return matches
1230
1231 def matchFile(self, buildfile):
1232 """
1233 Find the .bb file which matches the expression in 'buildfile'.
1234 Raise an error if multiple files
1235 """
1236 matches = self.matchFiles(buildfile)
1237 if len(matches) != 1:
1238 if matches:
1239 msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
1240 if matches:
1241 for f in matches:
1242 msg += "\n %s" % f
1243 parselog.error(msg)
1244 else:
1245 parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
1246 raise NoSpecificMatch
1247 return matches[0]
1248
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001249 def buildFile(self, buildfile, task):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001250 """
1251 Build the file matching regexp buildfile
1252 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001253 bb.event.fire(bb.event.BuildInit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001254
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001255 # Too many people use -b because they think it's how you normally
1256 # specify a target to be built, so show a warning
1257 bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
1258
1259 self.buildFileInternal(buildfile, task)
1260
1261 def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
1262 """
1263 Build the file matching regexp buildfile
1264 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001265
1266 # Parse the configuration here. We need to do it explicitly here since
1267 # buildFile() doesn't use the cache
1268 self.parseConfiguration()
1269
1270 # If we are told to do the None task then query the default task
1271 if (task == None):
1272 task = self.configuration.cmd
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001273 if not task.startswith("do_"):
1274 task = "do_%s" % task
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001275
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001276 fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001277 fn = self.matchFile(fn)
1278
1279 self.buildSetVars()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001280 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001281
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001282 bb_cache = bb.cache.Cache(self.databuilder, self.data_hash, self.caches_array)
1283
1284 infos = bb_cache.parse(fn, self.collection.get_file_appends(fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001285 infos = dict(infos)
1286
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001287 fn = bb.cache.realfn2virtual(fn, cls, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001288 try:
1289 info_array = infos[fn]
1290 except KeyError:
1291 bb.fatal("%s does not exist" % fn)
1292
1293 if info_array[0].skipped:
1294 bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
1295
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001296 self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001297
1298 # Tweak some variables
1299 item = info_array[0].pn
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001300 self.recipecaches[mc].ignored_dependencies = set()
1301 self.recipecaches[mc].bbfile_priority[fn] = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001302 self.configuration.limited_deps = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001303
1304 # Remove external dependencies
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001305 self.recipecaches[mc].task_deps[fn]['depends'] = {}
1306 self.recipecaches[mc].deps[fn] = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001307 self.recipecaches[mc].rundeps[fn] = defaultdict(list)
1308 self.recipecaches[mc].runrecs[fn] = defaultdict(list)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001309
1310 # Invalidate task for target if force mode active
1311 if self.configuration.force:
1312 logger.verbose("Invalidate task %s, %s", task, fn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001313 bb.parse.siggen.invalidate_task(task, self.recipecaches[mc], fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001314
1315 # Setup taskdata structure
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001316 taskdata = {}
1317 taskdata[mc] = bb.taskdata.TaskData(self.configuration.abort)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001318 taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001319
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001320 if quietlog:
1321 rqloglevel = bb.runqueue.logger.getEffectiveLevel()
1322 bb.runqueue.logger.setLevel(logging.WARNING)
1323
1324 buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
1325 if fireevents:
1326 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001327
1328 # Execute the runqueue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001329 runlist = [[mc, item, task, fn]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001330
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001331 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001332
1333 def buildFileIdle(server, rq, abort):
1334
1335 msg = None
1336 interrupted = 0
1337 if abort or self.state == state.forceshutdown:
1338 rq.finish_runqueue(True)
1339 msg = "Forced shutdown"
1340 interrupted = 2
1341 elif self.state == state.shutdown:
1342 rq.finish_runqueue(False)
1343 msg = "Stopped build"
1344 interrupted = 1
1345 failures = 0
1346 try:
1347 retval = rq.execute_runqueue()
1348 except runqueue.TaskFailure as exc:
1349 failures += len(exc.args)
1350 retval = False
1351 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001352 self.command.finishAsyncCommand(str(exc))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001353 if quietlog:
1354 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001355 return False
1356
1357 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001358 if fireevents:
1359 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 -05001360 self.command.finishAsyncCommand(msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001361 # We trashed self.recipecaches above
1362 self.parsecache_valid = False
1363 self.configuration.limited_deps = False
1364 bb.parse.siggen.reset(self.data)
1365 if quietlog:
1366 bb.runqueue.logger.setLevel(rqloglevel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001367 return False
1368 if retval is True:
1369 return True
1370 return retval
1371
1372 self.configuration.server_register_idlecallback(buildFileIdle, rq)
1373
1374 def buildTargets(self, targets, task):
1375 """
1376 Attempt to build the targets specified
1377 """
1378
1379 def buildTargetsIdle(server, rq, abort):
1380 msg = None
1381 interrupted = 0
1382 if abort or self.state == state.forceshutdown:
1383 rq.finish_runqueue(True)
1384 msg = "Forced shutdown"
1385 interrupted = 2
1386 elif self.state == state.shutdown:
1387 rq.finish_runqueue(False)
1388 msg = "Stopped build"
1389 interrupted = 1
1390 failures = 0
1391 try:
1392 retval = rq.execute_runqueue()
1393 except runqueue.TaskFailure as exc:
1394 failures += len(exc.args)
1395 retval = False
1396 except SystemExit as exc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001397 self.command.finishAsyncCommand(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001398 return False
1399
1400 if not retval:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001401 try:
1402 for mc in self.multiconfigs:
1403 bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
1404 finally:
1405 self.command.finishAsyncCommand(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001406 return False
1407 if retval is True:
1408 return True
1409 return retval
1410
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001411 self.reset_mtime_caches()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001412 self.buildSetVars()
1413
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001414 # If we are told to do the None task then query the default task
1415 if (task == None):
1416 task = self.configuration.cmd
1417
1418 if not task.startswith("do_"):
1419 task = "do_%s" % task
1420
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001421 packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
1422
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001423 bb.event.fire(bb.event.BuildInit(packages), self.data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001424
1425 taskdata, runlist = self.buildTaskData(targets, task, self.configuration.abort)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001426
1427 buildname = self.data.getVar("BUILDNAME", False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001428
1429 # make targets to always look as <target>:do_<task>
1430 ntargets = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001431 for target in runlist:
1432 if target[0]:
1433 ntargets.append("multiconfig:%s:%s:%s" % (target[0], target[1], target[2]))
1434 ntargets.append("%s:%s" % (target[1], target[2]))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001435
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001436 for mc in self.multiconfigs:
1437 bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001438
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001439 rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001440 if 'universe' in targets:
1441 rq.rqdata.warn_multi_bb = True
1442
1443 self.configuration.server_register_idlecallback(buildTargetsIdle, rq)
1444
1445
1446 def getAllKeysWithFlags(self, flaglist):
1447 dump = {}
1448 for k in self.data.keys():
1449 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001450 expand = True
1451 flags = self.data.getVarFlags(k)
1452 if flags and "func" in flags and "python" in flags:
1453 expand = False
1454 v = self.data.getVar(k, expand)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001455 if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
1456 dump[k] = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001457 'v' : str(v) ,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001458 'history' : self.data.varhistory.variable(k),
1459 }
1460 for d in flaglist:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001461 if flags and d in flags:
1462 dump[k][d] = flags[d]
1463 else:
1464 dump[k][d] = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001465 except Exception as e:
1466 print(e)
1467 return dump
1468
1469
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001470 def updateCacheSync(self):
1471 if self.state == state.running:
1472 return
1473
1474 # reload files for which we got notifications
1475 for p in self.inotify_modified_files:
1476 bb.parse.update_cache(p)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001477 if p in bb.parse.BBHandler.cached_statements:
1478 del bb.parse.BBHandler.cached_statements[p]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001479 self.inotify_modified_files = []
1480
1481 if not self.baseconfig_valid:
1482 logger.debug(1, "Reloading base configuration data")
1483 self.initConfigurationData()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001484 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001485
1486 # This is called for all async commands when self.state != running
1487 def updateCache(self):
1488 if self.state == state.running:
1489 return
1490
1491 if self.state in (state.shutdown, state.forceshutdown, state.error):
1492 if hasattr(self.parser, 'shutdown'):
1493 self.parser.shutdown(clean=False, force = True)
1494 raise bb.BBHandledException()
1495
1496 if self.state != state.parsing:
1497 self.updateCacheSync()
1498
1499 if self.state != state.parsing and not self.parsecache_valid:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001500 bb.parse.siggen.reset(self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001501 self.parseConfiguration ()
1502 if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001503 for mc in self.multiconfigs:
1504 bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001505
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001506 for mc in self.multiconfigs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001507 ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001508 self.recipecaches[mc].ignored_dependencies = set(ignore.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001509
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001510 for dep in self.configuration.extra_assume_provided:
1511 self.recipecaches[mc].ignored_dependencies.add(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001512
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001513 self.collection = CookerCollectFiles(self.bbfile_config_priorities)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001514 (filelist, masked, searchdirs) = self.collection.collect_bbfiles(self.data, self.data)
1515
1516 # Add inotify watches for directories searched for bb/bbappend files
1517 for dirent in searchdirs:
1518 self.add_filewatch([[dirent]], dirs=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001519
1520 self.parser = CookerParser(self, filelist, masked)
1521 self.parsecache_valid = True
1522
1523 self.state = state.parsing
1524
1525 if not self.parser.parse_next():
1526 collectlog.debug(1, "parsing complete")
1527 if self.parser.error:
1528 raise bb.BBHandledException()
1529 self.show_appends_with_no_recipes()
1530 self.handlePrefProviders()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001531 for mc in self.multiconfigs:
1532 self.recipecaches[mc].bbfile_priority = self.collection.collection_priorities(self.recipecaches[mc].pkg_fn, self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001533 self.state = state.running
1534
1535 # Send an event listing all stamps reachable after parsing
1536 # which the metadata may use to clean up stale data
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001537 for mc in self.multiconfigs:
1538 event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
1539 bb.event.fire(event, self.databuilder.mcdata[mc])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001540 return None
1541
1542 return True
1543
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001544 def checkPackages(self, pkgs_to_build, task=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001545
1546 # Return a copy, don't modify the original
1547 pkgs_to_build = pkgs_to_build[:]
1548
1549 if len(pkgs_to_build) == 0:
1550 raise NothingToBuild
1551
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001552 ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001553 for pkg in pkgs_to_build:
1554 if pkg in ignore:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001555 parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001556
1557 if 'world' in pkgs_to_build:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001558 pkgs_to_build.remove('world')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001559 for mc in self.multiconfigs:
1560 bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
1561 for t in self.recipecaches[mc].world_target:
1562 if mc:
1563 t = "multiconfig:" + mc + ":" + t
1564 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001565
1566 if 'universe' in pkgs_to_build:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001567 parselog.warning("The \"universe\" target is only intended for testing and may produce errors.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001568 parselog.debug(1, "collating packages for \"universe\"")
1569 pkgs_to_build.remove('universe')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001570 for mc in self.multiconfigs:
1571 for t in self.recipecaches[mc].universe_target:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001572 if task:
1573 foundtask = False
1574 for provider_fn in self.recipecaches[mc].providers[t]:
1575 if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
1576 foundtask = True
1577 break
1578 if not foundtask:
1579 bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
1580 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001581 if mc:
1582 t = "multiconfig:" + mc + ":" + t
1583 pkgs_to_build.append(t)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001584
1585 return pkgs_to_build
1586
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001587 def pre_serve(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001588 # We now are in our own process so we can call this here.
1589 # PRServ exits if its parent process exits
1590 self.handlePRServ()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001591 return
1592
1593 def post_serve(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001594 prserv.serv.auto_shutdown()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001595 bb.event.fire(CookerExit(), self.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001596
1597
1598 def shutdown(self, force = False):
1599 if force:
1600 self.state = state.forceshutdown
1601 else:
1602 self.state = state.shutdown
1603
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001604 if self.parser:
1605 self.parser.shutdown(clean=not force, force=force)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001606 self.notifier.stop()
1607 self.confignotifier.stop()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001608
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001609 def finishcommand(self):
1610 self.state = state.initial
1611
1612 def reset(self):
1613 self.initConfigurationData()
1614
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001615 def clientComplete(self):
1616 """Called when the client is done using the server"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001617 self.finishcommand()
1618 self.extraconfigdata = {}
1619 self.command.reset()
1620 self.databuilder.reset()
1621 self.data = self.databuilder.data
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001622
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001623
1624class CookerExit(bb.event.Event):
1625 """
1626 Notify clients of the Cooker shutdown
1627 """
1628
1629 def __init__(self):
1630 bb.event.Event.__init__(self)
1631
1632
1633class CookerCollectFiles(object):
1634 def __init__(self, priorities):
1635 self.bbappends = []
1636 self.bbfile_config_priorities = priorities
1637
1638 def calc_bbfile_priority( self, filename, matched = None ):
1639 for _, _, regex, pri in self.bbfile_config_priorities:
1640 if regex.match(filename):
1641 if matched != None:
1642 if not regex in matched:
1643 matched.add(regex)
1644 return pri
1645 return 0
1646
1647 def get_bbfiles(self):
1648 """Get list of default .bb files by reading out the current directory"""
1649 path = os.getcwd()
1650 contents = os.listdir(path)
1651 bbfiles = []
1652 for f in contents:
1653 if f.endswith(".bb"):
1654 bbfiles.append(os.path.abspath(os.path.join(path, f)))
1655 return bbfiles
1656
1657 def find_bbfiles(self, path):
1658 """Find all the .bb and .bbappend files in a directory"""
1659 found = []
1660 for dir, dirs, files in os.walk(path):
1661 for ignored in ('SCCS', 'CVS', '.svn'):
1662 if ignored in dirs:
1663 dirs.remove(ignored)
1664 found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
1665
1666 return found
1667
1668 def collect_bbfiles(self, config, eventdata):
1669 """Collect all available .bb build files"""
1670 masked = 0
1671
1672 collectlog.debug(1, "collecting .bb files")
1673
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001674 files = (config.getVar( "BBFILES") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001675 config.setVar("BBFILES", " ".join(files))
1676
1677 # Sort files by priority
1678 files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem) )
1679
1680 if not len(files):
1681 files = self.get_bbfiles()
1682
1683 if not len(files):
1684 collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
1685 bb.event.fire(CookerExit(), eventdata)
1686
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001687 # We need to track where we look so that we can add inotify watches. There
1688 # is no nice way to do this, this is horrid. We intercept the os.listdir()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001689 # (or os.scandir() for python 3.6+) calls while we run glob().
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001690 origlistdir = os.listdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001691 if hasattr(os, 'scandir'):
1692 origscandir = os.scandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001693 searchdirs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001694
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001695 def ourlistdir(d):
1696 searchdirs.append(d)
1697 return origlistdir(d)
1698
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001699 def ourscandir(d):
1700 searchdirs.append(d)
1701 return origscandir(d)
1702
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001703 os.listdir = ourlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001704 if hasattr(os, 'scandir'):
1705 os.scandir = ourscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001706 try:
1707 # Can't use set here as order is important
1708 newfiles = []
1709 for f in files:
1710 if os.path.isdir(f):
1711 dirfiles = self.find_bbfiles(f)
1712 for g in dirfiles:
1713 if g not in newfiles:
1714 newfiles.append(g)
1715 else:
1716 globbed = glob.glob(f)
1717 if not globbed and os.path.exists(f):
1718 globbed = [f]
1719 # glob gives files in order on disk. Sort to be deterministic.
1720 for g in sorted(globbed):
1721 if g not in newfiles:
1722 newfiles.append(g)
1723 finally:
1724 os.listdir = origlistdir
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001725 if hasattr(os, 'scandir'):
1726 os.scandir = origscandir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001727
1728 bbmask = config.getVar('BBMASK')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001729
1730 if bbmask:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001731 # First validate the individual regular expressions and ignore any
1732 # that do not compile
1733 bbmasks = []
1734 for mask in bbmask.split():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001735 # When constructing an older style single regex, it's possible for BBMASK
1736 # to end up beginning with '|', which matches and masks _everything_.
1737 if mask.startswith("|"):
1738 collectlog.warn("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
1739 mask = mask[1:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001740 try:
1741 re.compile(mask)
1742 bbmasks.append(mask)
1743 except sre_constants.error:
1744 collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
1745
1746 # Then validate the combined regular expressions. This should never
1747 # fail, but better safe than sorry...
1748 bbmask = "|".join(bbmasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001749 try:
1750 bbmask_compiled = re.compile(bbmask)
1751 except sre_constants.error:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001752 collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
1753 bbmask = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001754
1755 bbfiles = []
1756 bbappend = []
1757 for f in newfiles:
1758 if bbmask and bbmask_compiled.search(f):
1759 collectlog.debug(1, "skipping masked file %s", f)
1760 masked += 1
1761 continue
1762 if f.endswith('.bb'):
1763 bbfiles.append(f)
1764 elif f.endswith('.bbappend'):
1765 bbappend.append(f)
1766 else:
1767 collectlog.debug(1, "skipping %s: unknown file extension", f)
1768
1769 # Build a list of .bbappend files for each .bb file
1770 for f in bbappend:
1771 base = os.path.basename(f).replace('.bbappend', '.bb')
1772 self.bbappends.append((base, f))
1773
1774 # Find overlayed recipes
1775 # bbfiles will be in priority order which makes this easy
1776 bbfile_seen = dict()
1777 self.overlayed = defaultdict(list)
1778 for f in reversed(bbfiles):
1779 base = os.path.basename(f)
1780 if base not in bbfile_seen:
1781 bbfile_seen[base] = f
1782 else:
1783 topfile = bbfile_seen[base]
1784 self.overlayed[topfile].append(f)
1785
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001786 return (bbfiles, masked, searchdirs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001787
1788 def get_file_appends(self, fn):
1789 """
1790 Returns a list of .bbappend files to apply to fn
1791 """
1792 filelist = []
1793 f = os.path.basename(fn)
1794 for b in self.bbappends:
1795 (bbappend, filename) = b
1796 if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
1797 filelist.append(filename)
1798 return filelist
1799
1800 def collection_priorities(self, pkgfns, d):
1801
1802 priorities = {}
1803
1804 # Calculate priorities for each file
1805 matched = set()
1806 for p in pkgfns:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001807 realfn, cls, mc = bb.cache.virtualfn2realfn(p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001808 priorities[p] = self.calc_bbfile_priority(realfn, matched)
1809
1810 # Don't show the warning if the BBFILE_PATTERN did match .bbappend files
1811 unmatched = set()
1812 for _, _, regex, pri in self.bbfile_config_priorities:
1813 if not regex in matched:
1814 unmatched.add(regex)
1815
1816 def findmatch(regex):
1817 for b in self.bbappends:
1818 (bbfile, append) = b
1819 if regex.match(append):
1820 return True
1821 return False
1822
1823 for unmatch in unmatched.copy():
1824 if findmatch(unmatch):
1825 unmatched.remove(unmatch)
1826
1827 for collection, pattern, regex, _ in self.bbfile_config_priorities:
1828 if regex in unmatched:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001829 if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001830 collectlog.warning("No bb files matched BBFILE_PATTERN_%s '%s'" % (collection, pattern))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001831
1832 return priorities
1833
1834class ParsingFailure(Exception):
1835 def __init__(self, realexception, recipe):
1836 self.realexception = realexception
1837 self.recipe = recipe
1838 Exception.__init__(self, realexception, recipe)
1839
1840class Feeder(multiprocessing.Process):
1841 def __init__(self, jobs, to_parsers, quit):
1842 self.quit = quit
1843 self.jobs = jobs
1844 self.to_parsers = to_parsers
1845 multiprocessing.Process.__init__(self)
1846
1847 def run(self):
1848 while True:
1849 try:
1850 quit = self.quit.get_nowait()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001851 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001852 pass
1853 else:
1854 if quit == 'cancel':
1855 self.to_parsers.cancel_join_thread()
1856 break
1857
1858 try:
1859 job = self.jobs.pop()
1860 except IndexError:
1861 break
1862
1863 try:
1864 self.to_parsers.put(job, timeout=0.5)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001865 except queue.Full:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001866 self.jobs.insert(0, job)
1867 continue
1868
1869class Parser(multiprocessing.Process):
1870 def __init__(self, jobs, results, quit, init, profile):
1871 self.jobs = jobs
1872 self.results = results
1873 self.quit = quit
1874 self.init = init
1875 multiprocessing.Process.__init__(self)
1876 self.context = bb.utils.get_context().copy()
1877 self.handlers = bb.event.get_class_handlers().copy()
1878 self.profile = profile
1879
1880 def run(self):
1881
1882 if not self.profile:
1883 self.realrun()
1884 return
1885
1886 try:
1887 import cProfile as profile
1888 except:
1889 import profile
1890 prof = profile.Profile()
1891 try:
1892 profile.Profile.runcall(prof, self.realrun)
1893 finally:
1894 logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
1895 prof.dump_stats(logfile)
1896
1897 def realrun(self):
1898 if self.init:
1899 self.init()
1900
1901 pending = []
1902 while True:
1903 try:
1904 self.quit.get_nowait()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001905 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001906 pass
1907 else:
1908 self.results.cancel_join_thread()
1909 break
1910
1911 if pending:
1912 result = pending.pop()
1913 else:
1914 try:
1915 job = self.jobs.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001916 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001917 continue
1918
1919 if job is None:
1920 break
1921 result = self.parse(*job)
1922
1923 try:
1924 self.results.put(result, timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001925 except queue.Full:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001926 pending.append(result)
1927
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001928 def parse(self, filename, appends):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001929 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001930 # Record the filename we're parsing into any events generated
1931 def parse_filter(self, record):
1932 record.taskpid = bb.event.worker_pid
1933 record.fn = filename
1934 return True
1935
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001936 # Reset our environment and handlers to the original settings
1937 bb.utils.set_context(self.context.copy())
1938 bb.event.set_class_handlers(self.handlers.copy())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001939 bb.event.LogHandler.filter = parse_filter
1940
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001941 return True, self.bb_cache.parse(filename, appends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001942 except Exception as exc:
1943 tb = sys.exc_info()[2]
1944 exc.recipe = filename
1945 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
1946 return True, exc
1947 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
1948 # and for example a worker thread doesn't just exit on its own in response to
1949 # a SystemExit event for example.
1950 except BaseException as exc:
1951 return True, ParsingFailure(exc, filename)
1952
1953class CookerParser(object):
1954 def __init__(self, cooker, filelist, masked):
1955 self.filelist = filelist
1956 self.cooker = cooker
1957 self.cfgdata = cooker.data
1958 self.cfghash = cooker.data_hash
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001959 self.cfgbuilder = cooker.databuilder
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001960
1961 # Accounting statistics
1962 self.parsed = 0
1963 self.cached = 0
1964 self.error = 0
1965 self.masked = masked
1966
1967 self.skipped = 0
1968 self.virtuals = 0
1969 self.total = len(filelist)
1970
1971 self.current = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001972 self.process_names = []
1973
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001974 self.bb_cache = bb.cache.Cache(self.cfgbuilder, self.cfghash, cooker.caches_array)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001975 self.fromcache = []
1976 self.willparse = []
1977 for filename in self.filelist:
1978 appends = self.cooker.collection.get_file_appends(filename)
1979 if not self.bb_cache.cacheValid(filename, appends):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001980 self.willparse.append((filename, appends))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001981 else:
1982 self.fromcache.append((filename, appends))
1983 self.toparse = self.total - len(self.fromcache)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001984 self.progress_chunk = int(max(self.toparse / 100, 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001985
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001986 self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001987 multiprocessing.cpu_count()), len(self.willparse))
1988
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001989 self.start()
1990 self.haveshutdown = False
1991
1992 def start(self):
1993 self.results = self.load_cached()
1994 self.processes = []
1995 if self.toparse:
1996 bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
1997 def init():
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001998 Parser.bb_cache = self.bb_cache
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001999 bb.utils.set_process_name(multiprocessing.current_process().name)
2000 multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
2001 multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002002
2003 self.feeder_quit = multiprocessing.Queue(maxsize=1)
2004 self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes)
2005 self.jobs = multiprocessing.Queue(maxsize=self.num_processes)
2006 self.result_queue = multiprocessing.Queue()
2007 self.feeder = Feeder(self.willparse, self.jobs, self.feeder_quit)
2008 self.feeder.start()
2009 for i in range(0, self.num_processes):
2010 parser = Parser(self.jobs, self.result_queue, self.parser_quit, init, self.cooker.configuration.profile)
2011 parser.start()
2012 self.process_names.append(parser.name)
2013 self.processes.append(parser)
2014
2015 self.results = itertools.chain(self.results, self.parse_generator())
2016
2017 def shutdown(self, clean=True, force=False):
2018 if not self.toparse:
2019 return
2020 if self.haveshutdown:
2021 return
2022 self.haveshutdown = True
2023
2024 if clean:
2025 event = bb.event.ParseCompleted(self.cached, self.parsed,
2026 self.skipped, self.masked,
2027 self.virtuals, self.error,
2028 self.total)
2029
2030 bb.event.fire(event, self.cfgdata)
2031 self.feeder_quit.put(None)
2032 for process in self.processes:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002033 self.parser_quit.put(None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002034 else:
2035 self.feeder_quit.put('cancel')
2036
2037 self.parser_quit.cancel_join_thread()
2038 for process in self.processes:
2039 self.parser_quit.put(None)
2040
2041 self.jobs.cancel_join_thread()
2042
2043 for process in self.processes:
2044 if force:
2045 process.join(.1)
2046 process.terminate()
2047 else:
2048 process.join()
2049 self.feeder.join()
2050
2051 sync = threading.Thread(target=self.bb_cache.sync)
2052 sync.start()
2053 multiprocessing.util.Finalize(None, sync.join, exitpriority=-100)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002054 bb.codeparser.parser_cache_savemerge()
2055 bb.fetch.fetcher_parse_done()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002056 if self.cooker.configuration.profile:
2057 profiles = []
2058 for i in self.process_names:
2059 logfile = "profile-parse-%s.log" % i
2060 if os.path.exists(logfile):
2061 profiles.append(logfile)
2062
2063 pout = "profile-parse.log.processed"
2064 bb.utils.process_profilelog(profiles, pout = pout)
2065 print("Processed parsing statistics saved to %s" % (pout))
2066
2067 def load_cached(self):
2068 for filename, appends in self.fromcache:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002069 cached, infos = self.bb_cache.load(filename, appends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002070 yield not cached, infos
2071
2072 def parse_generator(self):
2073 while True:
2074 if self.parsed >= self.toparse:
2075 break
2076
2077 try:
2078 result = self.result_queue.get(timeout=0.25)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002079 except queue.Empty:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002080 pass
2081 else:
2082 value = result[1]
2083 if isinstance(value, BaseException):
2084 raise value
2085 else:
2086 yield result
2087
2088 def parse_next(self):
2089 result = []
2090 parsed = None
2091 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002092 parsed, result = next(self.results)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002093 except StopIteration:
2094 self.shutdown()
2095 return False
2096 except bb.BBHandledException as exc:
2097 self.error += 1
2098 logger.error('Failed to parse recipe: %s' % exc.recipe)
2099 self.shutdown(clean=False)
2100 return False
2101 except ParsingFailure as exc:
2102 self.error += 1
2103 logger.error('Unable to parse %s: %s' %
2104 (exc.recipe, bb.exceptions.to_string(exc.realexception)))
2105 self.shutdown(clean=False)
2106 return False
2107 except bb.parse.ParseError as exc:
2108 self.error += 1
2109 logger.error(str(exc))
2110 self.shutdown(clean=False)
2111 return False
2112 except bb.data_smart.ExpansionError as exc:
2113 self.error += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002114 bbdir = os.path.dirname(__file__) + os.sep
2115 etype, value, _ = sys.exc_info()
2116 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
2117 logger.error('ExpansionError during parsing %s', value.recipe,
2118 exc_info=(etype, value, tb))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002119 self.shutdown(clean=False)
2120 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002121 except Exception as exc:
2122 self.error += 1
2123 etype, value, tb = sys.exc_info()
2124 if hasattr(value, "recipe"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002125 logger.error('Unable to parse %s' % value.recipe,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002126 exc_info=(etype, value, exc.traceback))
2127 else:
2128 # Most likely, an exception occurred during raising an exception
2129 import traceback
2130 logger.error('Exception during parse: %s' % traceback.format_exc())
2131 self.shutdown(clean=False)
2132 return False
2133
2134 self.current += 1
2135 self.virtuals += len(result)
2136 if parsed:
2137 self.parsed += 1
2138 if self.parsed % self.progress_chunk == 0:
2139 bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
2140 self.cfgdata)
2141 else:
2142 self.cached += 1
2143
2144 for virtualfn, info_array in result:
2145 if info_array[0].skipped:
2146 self.skipped += 1
2147 self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002148 (fn, cls, mc) = bb.cache.virtualfn2realfn(virtualfn)
2149 self.bb_cache.add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002150 parsed=parsed, watcher = self.cooker.add_filewatch)
2151 return True
2152
2153 def reparse(self, filename):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002154 infos = self.bb_cache.parse(filename, self.cooker.collection.get_file_appends(filename))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002155 for vfn, info_array in infos:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002156 (fn, cls, mc) = bb.cache.virtualfn2realfn(vfn)
2157 self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)