cooker.py 95 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397
  1. #
  2. # Copyright (C) 2003, 2004 Chris Larson
  3. # Copyright (C) 2003, 2004 Phil Blundell
  4. # Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
  5. # Copyright (C) 2005 Holger Hans Peter Freyther
  6. # Copyright (C) 2005 ROAD GmbH
  7. # Copyright (C) 2006 - 2007 Richard Purdie
  8. #
  9. # SPDX-License-Identifier: GPL-2.0-only
  10. #
  11. import sys, os, glob, os.path, re, time
  12. import itertools
  13. import logging
  14. import multiprocessing
  15. import threading
  16. from io import StringIO, UnsupportedOperation
  17. from contextlib import closing
  18. from collections import defaultdict, namedtuple
  19. import bb, bb.exceptions, bb.command
  20. from bb import utils, data, parse, event, cache, providers, taskdata, runqueue, build
  21. import queue
  22. import signal
  23. import prserv.serv
  24. import pyinotify
  25. import json
  26. import pickle
  27. import codecs
  28. import hashserv
  29. logger = logging.getLogger("BitBake")
  30. collectlog = logging.getLogger("BitBake.Collection")
  31. buildlog = logging.getLogger("BitBake.Build")
  32. parselog = logging.getLogger("BitBake.Parsing")
  33. providerlog = logging.getLogger("BitBake.Provider")
  34. class NoSpecificMatch(bb.BBHandledException):
  35. """
  36. Exception raised when no or multiple file matches are found
  37. """
  38. class NothingToBuild(Exception):
  39. """
  40. Exception raised when there is nothing to build
  41. """
  42. class CollectionError(bb.BBHandledException):
  43. """
  44. Exception raised when layer configuration is incorrect
  45. """
  46. class state:
  47. initial, parsing, running, shutdown, forceshutdown, stopped, error = list(range(7))
  48. @classmethod
  49. def get_name(cls, code):
  50. for name in dir(cls):
  51. value = getattr(cls, name)
  52. if type(value) == type(cls.initial) and value == code:
  53. return name
  54. raise ValueError("Invalid status code: %s" % code)
  55. class SkippedPackage:
  56. def __init__(self, info = None, reason = None):
  57. self.pn = None
  58. self.skipreason = None
  59. self.provides = None
  60. self.rprovides = None
  61. if info:
  62. self.pn = info.pn
  63. self.skipreason = info.skipreason
  64. self.provides = info.provides
  65. self.rprovides = info.packages + info.rprovides
  66. for package in info.packages:
  67. self.rprovides += info.rprovides_pkg[package]
  68. elif reason:
  69. self.skipreason = reason
  70. class CookerFeatures(object):
  71. _feature_list = [HOB_EXTRA_CACHES, BASEDATASTORE_TRACKING, SEND_SANITYEVENTS] = list(range(3))
  72. def __init__(self):
  73. self._features=set()
  74. def setFeature(self, f):
  75. # validate we got a request for a feature we support
  76. if f not in CookerFeatures._feature_list:
  77. return
  78. self._features.add(f)
  79. def __contains__(self, f):
  80. return f in self._features
  81. def __iter__(self):
  82. return self._features.__iter__()
  83. def __next__(self):
  84. return next(self._features)
  85. class EventWriter:
  86. def __init__(self, cooker, eventfile):
  87. self.file_inited = None
  88. self.cooker = cooker
  89. self.eventfile = eventfile
  90. self.event_queue = []
  91. def write_event(self, event):
  92. with open(self.eventfile, "a") as f:
  93. try:
  94. str_event = codecs.encode(pickle.dumps(event), 'base64').decode('utf-8')
  95. f.write("%s\n" % json.dumps({"class": event.__module__ + "." + event.__class__.__name__,
  96. "vars": str_event}))
  97. except Exception as err:
  98. import traceback
  99. print(err, traceback.format_exc())
  100. def send(self, event):
  101. if self.file_inited:
  102. # we have the file, just write the event
  103. self.write_event(event)
  104. else:
  105. # init on bb.event.BuildStarted
  106. name = "%s.%s" % (event.__module__, event.__class__.__name__)
  107. if name in ("bb.event.BuildStarted", "bb.cooker.CookerExit"):
  108. with open(self.eventfile, "w") as f:
  109. f.write("%s\n" % json.dumps({ "allvariables" : self.cooker.getAllKeysWithFlags(["doc", "func"])}))
  110. self.file_inited = True
  111. # write pending events
  112. for evt in self.event_queue:
  113. self.write_event(evt)
  114. # also write the current event
  115. self.write_event(event)
  116. else:
  117. # queue all events until the file is inited
  118. self.event_queue.append(event)
  119. #============================================================================#
  120. # BBCooker
  121. #============================================================================#
  122. class BBCooker:
  123. """
  124. Manages one bitbake build run
  125. """
  126. def __init__(self, featureSet=None, idleCallBackRegister=None):
  127. self.recipecaches = None
  128. self.eventlog = None
  129. self.skiplist = {}
  130. self.featureset = CookerFeatures()
  131. if featureSet:
  132. for f in featureSet:
  133. self.featureset.setFeature(f)
  134. self.orig_syspath = sys.path.copy()
  135. self.orig_sysmodules = [*sys.modules]
  136. self.configuration = bb.cookerdata.CookerConfiguration()
  137. self.idleCallBackRegister = idleCallBackRegister
  138. bb.debug(1, "BBCooker starting %s" % time.time())
  139. sys.stdout.flush()
  140. self.configwatcher = None
  141. self.confignotifier = None
  142. self.watchmask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CREATE | pyinotify.IN_DELETE | \
  143. pyinotify.IN_DELETE_SELF | pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF | \
  144. pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO
  145. self.watcher = None
  146. self.notifier = None
  147. # If being called by something like tinfoil, we need to clean cached data
  148. # which may now be invalid
  149. bb.parse.clear_cache()
  150. bb.parse.BBHandler.cached_statements = {}
  151. self.ui_cmdline = None
  152. self.hashserv = None
  153. self.hashservaddr = None
  154. self.inotify_modified_files = []
  155. def _process_inotify_updates(server, cooker, halt):
  156. cooker.process_inotify_updates()
  157. return 1.0
  158. self.idleCallBackRegister(_process_inotify_updates, self)
  159. # TOSTOP must not be set or our children will hang when they output
  160. try:
  161. fd = sys.stdout.fileno()
  162. if os.isatty(fd):
  163. import termios
  164. tcattr = termios.tcgetattr(fd)
  165. if tcattr[3] & termios.TOSTOP:
  166. buildlog.info("The terminal had the TOSTOP bit set, clearing...")
  167. tcattr[3] = tcattr[3] & ~termios.TOSTOP
  168. termios.tcsetattr(fd, termios.TCSANOW, tcattr)
  169. except UnsupportedOperation:
  170. pass
  171. self.command = bb.command.Command(self)
  172. self.state = state.initial
  173. self.parser = None
  174. signal.signal(signal.SIGTERM, self.sigterm_exception)
  175. # Let SIGHUP exit as SIGTERM
  176. signal.signal(signal.SIGHUP, self.sigterm_exception)
  177. bb.debug(1, "BBCooker startup complete %s" % time.time())
  178. sys.stdout.flush()
  179. def init_configdata(self):
  180. if not hasattr(self, "data"):
  181. self.initConfigurationData()
  182. bb.debug(1, "BBCooker parsed base configuration %s" % time.time())
  183. sys.stdout.flush()
  184. self.handlePRServ()
  185. def setupConfigWatcher(self):
  186. if self.configwatcher:
  187. self.configwatcher.close()
  188. self.confignotifier = None
  189. self.configwatcher = None
  190. self.configwatcher = pyinotify.WatchManager()
  191. self.configwatcher.bbseen = set()
  192. self.configwatcher.bbwatchedfiles = set()
  193. self.confignotifier = pyinotify.Notifier(self.configwatcher, self.config_notifications)
  194. def setupParserWatcher(self):
  195. if self.watcher:
  196. self.watcher.close()
  197. self.notifier = None
  198. self.watcher = None
  199. self.watcher = pyinotify.WatchManager()
  200. self.watcher.bbseen = set()
  201. self.watcher.bbwatchedfiles = set()
  202. self.notifier = pyinotify.Notifier(self.watcher, self.notifications)
  203. def process_inotify_updates(self):
  204. for n in [self.confignotifier, self.notifier]:
  205. if n and n.check_events(timeout=0):
  206. # read notified events and enqeue them
  207. n.read_events()
  208. n.process_events()
  209. def config_notifications(self, event):
  210. if event.maskname == "IN_Q_OVERFLOW":
  211. bb.warn("inotify event queue overflowed, invalidating caches.")
  212. self.parsecache_valid = False
  213. self.baseconfig_valid = False
  214. bb.parse.clear_cache()
  215. return
  216. if not event.pathname in self.configwatcher.bbwatchedfiles:
  217. return
  218. if "IN_ISDIR" in event.maskname:
  219. if "IN_CREATE" in event.maskname or "IN_DELETE" in event.maskname:
  220. if event.pathname in self.configwatcher.bbseen:
  221. self.configwatcher.bbseen.remove(event.pathname)
  222. # Could remove all entries starting with the directory but for now...
  223. bb.parse.clear_cache()
  224. if not event.pathname in self.inotify_modified_files:
  225. self.inotify_modified_files.append(event.pathname)
  226. self.baseconfig_valid = False
  227. def notifications(self, event):
  228. if event.maskname == "IN_Q_OVERFLOW":
  229. bb.warn("inotify event queue overflowed, invalidating caches.")
  230. self.parsecache_valid = False
  231. bb.parse.clear_cache()
  232. return
  233. if event.pathname.endswith("bitbake-cookerdaemon.log") \
  234. or event.pathname.endswith("bitbake.lock"):
  235. return
  236. if "IN_ISDIR" in event.maskname:
  237. if "IN_CREATE" in event.maskname or "IN_DELETE" in event.maskname:
  238. if event.pathname in self.watcher.bbseen:
  239. self.watcher.bbseen.remove(event.pathname)
  240. # Could remove all entries starting with the directory but for now...
  241. bb.parse.clear_cache()
  242. if not event.pathname in self.inotify_modified_files:
  243. self.inotify_modified_files.append(event.pathname)
  244. self.parsecache_valid = False
  245. def add_filewatch(self, deps, watcher=None, dirs=False):
  246. if not watcher:
  247. watcher = self.watcher
  248. for i in deps:
  249. watcher.bbwatchedfiles.add(i[0])
  250. if dirs:
  251. f = i[0]
  252. else:
  253. f = os.path.dirname(i[0])
  254. if f in watcher.bbseen:
  255. continue
  256. watcher.bbseen.add(f)
  257. watchtarget = None
  258. while True:
  259. # We try and add watches for files that don't exist but if they did, would influence
  260. # the parser. The parent directory of these files may not exist, in which case we need
  261. # to watch any parent that does exist for changes.
  262. try:
  263. watcher.add_watch(f, self.watchmask, quiet=False)
  264. if watchtarget:
  265. watcher.bbwatchedfiles.add(watchtarget)
  266. break
  267. except pyinotify.WatchManagerError as e:
  268. if 'ENOENT' in str(e):
  269. watchtarget = f
  270. f = os.path.dirname(f)
  271. if f in watcher.bbseen:
  272. break
  273. watcher.bbseen.add(f)
  274. continue
  275. if 'ENOSPC' in str(e):
  276. providerlog.error("No space left on device or exceeds fs.inotify.max_user_watches?")
  277. providerlog.error("To check max_user_watches: sysctl -n fs.inotify.max_user_watches.")
  278. providerlog.error("To modify max_user_watches: sysctl -n -w fs.inotify.max_user_watches=<value>.")
  279. providerlog.error("Root privilege is required to modify max_user_watches.")
  280. raise
  281. def sigterm_exception(self, signum, stackframe):
  282. if signum == signal.SIGTERM:
  283. bb.warn("Cooker received SIGTERM, shutting down...")
  284. elif signum == signal.SIGHUP:
  285. bb.warn("Cooker received SIGHUP, shutting down...")
  286. self.state = state.forceshutdown
  287. def setFeatures(self, features):
  288. # we only accept a new feature set if we're in state initial, so we can reset without problems
  289. if not self.state in [state.initial, state.shutdown, state.forceshutdown, state.stopped, state.error]:
  290. raise Exception("Illegal state for feature set change")
  291. original_featureset = list(self.featureset)
  292. for feature in features:
  293. self.featureset.setFeature(feature)
  294. bb.debug(1, "Features set %s (was %s)" % (original_featureset, list(self.featureset)))
  295. if (original_featureset != list(self.featureset)) and self.state != state.error and hasattr(self, "data"):
  296. self.reset()
  297. def initConfigurationData(self):
  298. self.state = state.initial
  299. self.caches_array = []
  300. sys.path = self.orig_syspath.copy()
  301. for mod in [*sys.modules]:
  302. if mod not in self.orig_sysmodules:
  303. del sys.modules[mod]
  304. self.setupConfigWatcher()
  305. # Need to preserve BB_CONSOLELOG over resets
  306. consolelog = None
  307. if hasattr(self, "data"):
  308. consolelog = self.data.getVar("BB_CONSOLELOG")
  309. if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
  310. self.enableDataTracking()
  311. all_extra_cache_names = []
  312. # We hardcode all known cache types in a single place, here.
  313. if CookerFeatures.HOB_EXTRA_CACHES in self.featureset:
  314. all_extra_cache_names.append("bb.cache_extra:HobRecipeInfo")
  315. caches_name_array = ['bb.cache:CoreRecipeInfo'] + all_extra_cache_names
  316. # At least CoreRecipeInfo will be loaded, so caches_array will never be empty!
  317. # This is the entry point, no further check needed!
  318. for var in caches_name_array:
  319. try:
  320. module_name, cache_name = var.split(':')
  321. module = __import__(module_name, fromlist=(cache_name,))
  322. self.caches_array.append(getattr(module, cache_name))
  323. except ImportError as exc:
  324. logger.critical("Unable to import extra RecipeInfo '%s' from '%s': %s" % (cache_name, module_name, exc))
  325. raise bb.BBHandledException()
  326. self.databuilder = bb.cookerdata.CookerDataBuilder(self.configuration, False)
  327. self.databuilder.parseBaseConfiguration()
  328. self.data = self.databuilder.data
  329. self.data_hash = self.databuilder.data_hash
  330. self.extraconfigdata = {}
  331. if consolelog:
  332. self.data.setVar("BB_CONSOLELOG", consolelog)
  333. self.data.setVar('BB_CMDLINE', self.ui_cmdline)
  334. if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
  335. self.disableDataTracking()
  336. for mc in self.databuilder.mcdata.values():
  337. mc.renameVar("__depends", "__base_depends")
  338. self.add_filewatch(mc.getVar("__base_depends", False), self.configwatcher)
  339. mc.setVar("__bbclasstype", "recipe")
  340. self.baseconfig_valid = True
  341. self.parsecache_valid = False
  342. def handlePRServ(self):
  343. # Setup a PR Server based on the new configuration
  344. try:
  345. self.prhost = prserv.serv.auto_start(self.data)
  346. except prserv.serv.PRServiceConfigError as e:
  347. bb.fatal("Unable to start PR Server, exiting, check the bitbake-cookerdaemon.log")
  348. if self.data.getVar("BB_HASHSERVE") == "auto":
  349. # Create a new hash server bound to a unix domain socket
  350. if not self.hashserv:
  351. dbfile = (self.data.getVar("PERSISTENT_DIR") or self.data.getVar("CACHE")) + "/hashserv.db"
  352. upstream = self.data.getVar("BB_HASHSERVE_UPSTREAM") or None
  353. if upstream:
  354. import socket
  355. try:
  356. sock = socket.create_connection(upstream.split(":"), 5)
  357. sock.close()
  358. except socket.error as e:
  359. bb.warn("BB_HASHSERVE_UPSTREAM is not valid, unable to connect hash equivalence server at '%s': %s"
  360. % (upstream, repr(e)))
  361. self.hashservaddr = "unix://%s/hashserve.sock" % self.data.getVar("TOPDIR")
  362. self.hashserv = hashserv.create_server(
  363. self.hashservaddr,
  364. dbfile,
  365. sync=False,
  366. upstream=upstream,
  367. )
  368. self.hashserv.serve_as_process()
  369. self.data.setVar("BB_HASHSERVE", self.hashservaddr)
  370. self.databuilder.origdata.setVar("BB_HASHSERVE", self.hashservaddr)
  371. self.databuilder.data.setVar("BB_HASHSERVE", self.hashservaddr)
  372. for mc in self.databuilder.mcdata:
  373. self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.hashservaddr)
  374. bb.parse.init_parser(self.data)
  375. def enableDataTracking(self):
  376. self.configuration.tracking = True
  377. if hasattr(self, "data"):
  378. self.data.enableTracking()
  379. def disableDataTracking(self):
  380. self.configuration.tracking = False
  381. if hasattr(self, "data"):
  382. self.data.disableTracking()
  383. def parseConfiguration(self):
  384. self.updateCacheSync()
  385. # Change nice level if we're asked to
  386. nice = self.data.getVar("BB_NICE_LEVEL")
  387. if nice:
  388. curnice = os.nice(0)
  389. nice = int(nice) - curnice
  390. buildlog.verbose("Renice to %s " % os.nice(nice))
  391. if self.recipecaches:
  392. del self.recipecaches
  393. self.multiconfigs = self.databuilder.mcdata.keys()
  394. self.recipecaches = {}
  395. for mc in self.multiconfigs:
  396. self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
  397. self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
  398. self.parsecache_valid = False
  399. def updateConfigOpts(self, options, environment, cmdline):
  400. self.ui_cmdline = cmdline
  401. clean = True
  402. for o in options:
  403. if o in ['prefile', 'postfile']:
  404. # Only these options may require a reparse
  405. try:
  406. if getattr(self.configuration, o) == options[o]:
  407. # Value is the same, no need to mark dirty
  408. continue
  409. except AttributeError:
  410. pass
  411. logger.debug("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
  412. print("Marking as dirty due to '%s' option change to '%s'" % (o, options[o]))
  413. clean = False
  414. if hasattr(self.configuration, o):
  415. setattr(self.configuration, o, options[o])
  416. if self.configuration.writeeventlog:
  417. if self.eventlog and self.eventlog[0] != self.configuration.writeeventlog:
  418. bb.event.unregister_UIHhandler(self.eventlog[1])
  419. if not self.eventlog or self.eventlog[0] != self.configuration.writeeventlog:
  420. # we log all events to a file if so directed
  421. # register the log file writer as UI Handler
  422. writer = EventWriter(self, self.configuration.writeeventlog)
  423. EventLogWriteHandler = namedtuple('EventLogWriteHandler', ['event'])
  424. self.eventlog = (self.configuration.writeeventlog, bb.event.register_UIHhandler(EventLogWriteHandler(writer)))
  425. bb.msg.loggerDefaultLogLevel = self.configuration.default_loglevel
  426. bb.msg.loggerDefaultDomains = self.configuration.debug_domains
  427. if hasattr(self, "data"):
  428. origenv = bb.data.init()
  429. for k in environment:
  430. origenv.setVar(k, environment[k])
  431. self.data.setVar("BB_ORIGENV", origenv)
  432. for k in bb.utils.approved_variables():
  433. if k in environment and k not in self.configuration.env:
  434. logger.debug("Updating new environment variable %s to %s" % (k, environment[k]))
  435. self.configuration.env[k] = environment[k]
  436. clean = False
  437. if k in self.configuration.env and k not in environment:
  438. logger.debug("Updating environment variable %s (deleted)" % (k))
  439. del self.configuration.env[k]
  440. clean = False
  441. if k not in self.configuration.env and k not in environment:
  442. continue
  443. if environment[k] != self.configuration.env[k]:
  444. logger.debug("Updating environment variable %s from %s to %s" % (k, self.configuration.env[k], environment[k]))
  445. self.configuration.env[k] = environment[k]
  446. clean = False
  447. # Now update all the variables not in the datastore to match
  448. self.configuration.env = environment
  449. if not clean:
  450. logger.debug("Base environment change, triggering reparse")
  451. self.reset()
  452. def runCommands(self, server, data, halt):
  453. """
  454. Run any queued asynchronous command
  455. This is done by the idle handler so it runs in true context rather than
  456. tied to any UI.
  457. """
  458. return self.command.runAsyncCommand()
  459. def showVersions(self):
  460. (latest_versions, preferred_versions, required) = self.findProviders()
  461. logger.plain("%-35s %25s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version", "Required Version")
  462. logger.plain("%-35s %25s %25s %25s\n", "===========", "==============", "=================", "================")
  463. for p in sorted(self.recipecaches[''].pkg_pn):
  464. preferred = preferred_versions[p]
  465. latest = latest_versions[p]
  466. requiredstr = ""
  467. preferredstr = ""
  468. if required[p]:
  469. if preferred[0] is not None:
  470. requiredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
  471. else:
  472. bb.fatal("REQUIRED_VERSION of package %s not available" % p)
  473. else:
  474. preferredstr = preferred[0][0] + ":" + preferred[0][1] + '-' + preferred[0][2]
  475. lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
  476. if preferred == latest:
  477. preferredstr = ""
  478. logger.plain("%-35s %25s %25s %25s", p, lateststr, preferredstr, requiredstr)
  479. def showEnvironment(self, buildfile=None, pkgs_to_build=None):
  480. """
  481. Show the outer or per-recipe environment
  482. """
  483. fn = None
  484. envdata = None
  485. mc = ''
  486. if not pkgs_to_build:
  487. pkgs_to_build = []
  488. orig_tracking = self.configuration.tracking
  489. if not orig_tracking:
  490. self.enableDataTracking()
  491. self.reset()
  492. # reset() resets to the UI requested value so we have to redo this
  493. self.enableDataTracking()
  494. def mc_base(p):
  495. if p.startswith('mc:'):
  496. s = p.split(':')
  497. if len(s) == 2:
  498. return s[1]
  499. return None
  500. 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. fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
  505. fn = self.matchFile(fn, mc)
  506. fn = bb.cache.realfn2virtual(fn, cls, mc)
  507. elif len(pkgs_to_build) == 1:
  508. mc = mc_base(pkgs_to_build[0])
  509. if not mc:
  510. ignore = self.data.getVar("ASSUME_PROVIDED") or ""
  511. if pkgs_to_build[0] in set(ignore.split()):
  512. bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
  513. taskdata, runlist = self.buildTaskData(pkgs_to_build, None, self.configuration.halt, allowincomplete=True)
  514. mc = runlist[0][0]
  515. fn = runlist[0][3]
  516. if fn:
  517. try:
  518. bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
  519. envdata = bb_caches[mc].loadDataFull(fn, self.collections[mc].get_file_appends(fn))
  520. except Exception as e:
  521. parselog.exception("Unable to read %s", fn)
  522. raise
  523. else:
  524. if not mc in self.databuilder.mcdata:
  525. bb.fatal('Not multiconfig named "%s" found' % mc)
  526. envdata = self.databuilder.mcdata[mc]
  527. data.expandKeys(envdata)
  528. parse.ast.runAnonFuncs(envdata)
  529. # Display history
  530. with closing(StringIO()) as env:
  531. self.data.inchistory.emit(env)
  532. logger.plain(env.getvalue())
  533. # emit variables and shell functions
  534. with closing(StringIO()) as env:
  535. data.emit_env(env, envdata, True)
  536. logger.plain(env.getvalue())
  537. # emit the metadata which isn't valid shell
  538. for e in sorted(envdata.keys()):
  539. if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
  540. logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
  541. if not orig_tracking:
  542. self.disableDataTracking()
  543. self.reset()
  544. def buildTaskData(self, pkgs_to_build, task, halt, allowincomplete=False):
  545. """
  546. Prepare a runqueue and taskdata object for iteration over pkgs_to_build
  547. """
  548. bb.event.fire(bb.event.TreeDataPreparationStarted(), self.data)
  549. # A task of None means use the default task
  550. if task is None:
  551. task = self.configuration.cmd
  552. if not task.startswith("do_"):
  553. task = "do_%s" % task
  554. targetlist = self.checkPackages(pkgs_to_build, task)
  555. fulltargetlist = []
  556. defaulttask_implicit = ''
  557. defaulttask_explicit = False
  558. wildcard = False
  559. # Wild card expansion:
  560. # Replace string such as "mc:*:bash"
  561. # into "mc:A:bash mc:B:bash bash"
  562. for k in targetlist:
  563. if k.startswith("mc:") and k.count(':') >= 2:
  564. if wildcard:
  565. bb.fatal('multiconfig conflict')
  566. if k.split(":")[1] == "*":
  567. wildcard = True
  568. for mc in self.multiconfigs:
  569. if mc:
  570. fulltargetlist.append(k.replace('*', mc))
  571. # implicit default task
  572. else:
  573. defaulttask_implicit = k.split(":")[2]
  574. else:
  575. fulltargetlist.append(k)
  576. else:
  577. defaulttask_explicit = True
  578. fulltargetlist.append(k)
  579. if not defaulttask_explicit and defaulttask_implicit != '':
  580. fulltargetlist.append(defaulttask_implicit)
  581. bb.debug(1,"Target list: %s" % (str(fulltargetlist)))
  582. taskdata = {}
  583. localdata = {}
  584. for mc in self.multiconfigs:
  585. taskdata[mc] = bb.taskdata.TaskData(halt, skiplist=self.skiplist, allowincomplete=allowincomplete)
  586. localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
  587. bb.data.expandKeys(localdata[mc])
  588. current = 0
  589. runlist = []
  590. for k in fulltargetlist:
  591. origk = k
  592. mc = ""
  593. if k.startswith("mc:") and k.count(':') >= 2:
  594. mc = k.split(":")[1]
  595. k = ":".join(k.split(":")[2:])
  596. ktask = task
  597. if ":do_" in k:
  598. k2 = k.split(":do_")
  599. k = k2[0]
  600. ktask = k2[1]
  601. if mc not in self.multiconfigs:
  602. bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named %s" % (origk, mc))
  603. taskdata[mc].add_provider(localdata[mc], self.recipecaches[mc], k)
  604. current += 1
  605. if not ktask.startswith("do_"):
  606. ktask = "do_%s" % ktask
  607. if k not in taskdata[mc].build_targets or not taskdata[mc].build_targets[k]:
  608. # e.g. in ASSUME_PROVIDED
  609. continue
  610. fn = taskdata[mc].build_targets[k][0]
  611. runlist.append([mc, k, ktask, fn])
  612. bb.event.fire(bb.event.TreeDataPreparationProgress(current, len(fulltargetlist)), self.data)
  613. havemc = False
  614. for mc in self.multiconfigs:
  615. if taskdata[mc].get_mcdepends():
  616. havemc = True
  617. # No need to do check providers if there are no mcdeps or not an mc build
  618. if havemc or len(self.multiconfigs) > 1:
  619. seen = set()
  620. new = True
  621. # Make sure we can provide the multiconfig dependency
  622. while new:
  623. mcdeps = set()
  624. # Add unresolved first, so we can get multiconfig indirect dependencies on time
  625. for mc in self.multiconfigs:
  626. taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
  627. mcdeps |= set(taskdata[mc].get_mcdepends())
  628. new = False
  629. for k in mcdeps:
  630. if k in seen:
  631. continue
  632. l = k.split(':')
  633. depmc = l[2]
  634. if depmc not in self.multiconfigs:
  635. bb.fatal("Multiconfig dependency %s depends on nonexistent multiconfig configuration named configuration %s" % (k,depmc))
  636. else:
  637. logger.debug("Adding providers for multiconfig dependency %s" % l[3])
  638. taskdata[depmc].add_provider(localdata[depmc], self.recipecaches[depmc], l[3])
  639. seen.add(k)
  640. new = True
  641. for mc in self.multiconfigs:
  642. taskdata[mc].add_unresolved(localdata[mc], self.recipecaches[mc])
  643. bb.event.fire(bb.event.TreeDataPreparationCompleted(len(fulltargetlist)), self.data)
  644. return taskdata, runlist
  645. def prepareTreeData(self, pkgs_to_build, task):
  646. """
  647. Prepare a runqueue and taskdata object for iteration over pkgs_to_build
  648. """
  649. # We set halt to False here to prevent unbuildable targets raising
  650. # an exception when we're just generating data
  651. taskdata, runlist = self.buildTaskData(pkgs_to_build, task, False, allowincomplete=True)
  652. return runlist, taskdata
  653. ######## WARNING : this function requires cache_extra to be enabled ########
  654. def generateTaskDepTreeData(self, pkgs_to_build, task):
  655. """
  656. Create a dependency graph of pkgs_to_build including reverse dependency
  657. information.
  658. """
  659. if not task.startswith("do_"):
  660. task = "do_%s" % task
  661. runlist, taskdata = self.prepareTreeData(pkgs_to_build, task)
  662. rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
  663. rq.rqdata.prepare()
  664. return self.buildDependTree(rq, taskdata)
  665. @staticmethod
  666. def add_mc_prefix(mc, pn):
  667. if mc:
  668. return "mc:%s:%s" % (mc, pn)
  669. return pn
  670. def buildDependTree(self, rq, taskdata):
  671. seen_fns = []
  672. depend_tree = {}
  673. depend_tree["depends"] = {}
  674. depend_tree["tdepends"] = {}
  675. depend_tree["pn"] = {}
  676. depend_tree["rdepends-pn"] = {}
  677. depend_tree["packages"] = {}
  678. depend_tree["rdepends-pkg"] = {}
  679. depend_tree["rrecs-pkg"] = {}
  680. depend_tree['providermap'] = {}
  681. depend_tree["layer-priorities"] = self.bbfile_config_priorities
  682. for mc in taskdata:
  683. for name, fn in list(taskdata[mc].get_providermap().items()):
  684. pn = self.recipecaches[mc].pkg_fn[fn]
  685. pn = self.add_mc_prefix(mc, pn)
  686. if name != pn:
  687. version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[fn]
  688. depend_tree['providermap'][name] = (pn, version)
  689. for tid in rq.rqdata.runtaskentries:
  690. (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
  691. pn = self.recipecaches[mc].pkg_fn[taskfn]
  692. pn = self.add_mc_prefix(mc, pn)
  693. version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
  694. if pn not in depend_tree["pn"]:
  695. depend_tree["pn"][pn] = {}
  696. depend_tree["pn"][pn]["filename"] = taskfn
  697. depend_tree["pn"][pn]["version"] = version
  698. depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
  699. # if we have extra caches, list all attributes they bring in
  700. extra_info = []
  701. for cache_class in self.caches_array:
  702. if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
  703. cachefields = getattr(cache_class, 'cachefields', [])
  704. extra_info = extra_info + cachefields
  705. # for all attributes stored, add them to the dependency tree
  706. for ei in extra_info:
  707. depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
  708. dotname = "%s.%s" % (pn, bb.runqueue.taskname_from_tid(tid))
  709. if not dotname in depend_tree["tdepends"]:
  710. depend_tree["tdepends"][dotname] = []
  711. for dep in rq.rqdata.runtaskentries[tid].depends:
  712. (depmc, depfn, _, deptaskfn) = bb.runqueue.split_tid_mcfn(dep)
  713. deppn = self.recipecaches[depmc].pkg_fn[deptaskfn]
  714. if depmc:
  715. depmc = "mc:" + depmc + ":"
  716. depend_tree["tdepends"][dotname].append("%s%s.%s" % (depmc, deppn, bb.runqueue.taskname_from_tid(dep)))
  717. if taskfn not in seen_fns:
  718. seen_fns.append(taskfn)
  719. packages = []
  720. depend_tree["depends"][pn] = []
  721. for dep in taskdata[mc].depids[taskfn]:
  722. depend_tree["depends"][pn].append(dep)
  723. depend_tree["rdepends-pn"][pn] = []
  724. for rdep in taskdata[mc].rdepids[taskfn]:
  725. depend_tree["rdepends-pn"][pn].append(rdep)
  726. rdepends = self.recipecaches[mc].rundeps[taskfn]
  727. for package in rdepends:
  728. depend_tree["rdepends-pkg"][package] = []
  729. for rdepend in rdepends[package]:
  730. depend_tree["rdepends-pkg"][package].append(rdepend)
  731. packages.append(package)
  732. rrecs = self.recipecaches[mc].runrecs[taskfn]
  733. for package in rrecs:
  734. depend_tree["rrecs-pkg"][package] = []
  735. for rdepend in rrecs[package]:
  736. depend_tree["rrecs-pkg"][package].append(rdepend)
  737. if not package in packages:
  738. packages.append(package)
  739. for package in packages:
  740. if package not in depend_tree["packages"]:
  741. depend_tree["packages"][package] = {}
  742. depend_tree["packages"][package]["pn"] = pn
  743. depend_tree["packages"][package]["filename"] = taskfn
  744. depend_tree["packages"][package]["version"] = version
  745. return depend_tree
  746. ######## WARNING : this function requires cache_extra to be enabled ########
  747. def generatePkgDepTreeData(self, pkgs_to_build, task):
  748. """
  749. Create a dependency tree of pkgs_to_build, returning the data.
  750. """
  751. if not task.startswith("do_"):
  752. task = "do_%s" % task
  753. _, taskdata = self.prepareTreeData(pkgs_to_build, task)
  754. seen_fns = []
  755. depend_tree = {}
  756. depend_tree["depends"] = {}
  757. depend_tree["pn"] = {}
  758. depend_tree["rdepends-pn"] = {}
  759. depend_tree["rdepends-pkg"] = {}
  760. depend_tree["rrecs-pkg"] = {}
  761. # if we have extra caches, list all attributes they bring in
  762. extra_info = []
  763. for cache_class in self.caches_array:
  764. if type(cache_class) is type and issubclass(cache_class, bb.cache.RecipeInfoCommon) and hasattr(cache_class, 'cachefields'):
  765. cachefields = getattr(cache_class, 'cachefields', [])
  766. extra_info = extra_info + cachefields
  767. tids = []
  768. for mc in taskdata:
  769. for tid in taskdata[mc].taskentries:
  770. tids.append(tid)
  771. for tid in tids:
  772. (mc, fn, taskname, taskfn) = bb.runqueue.split_tid_mcfn(tid)
  773. pn = self.recipecaches[mc].pkg_fn[taskfn]
  774. pn = self.add_mc_prefix(mc, pn)
  775. if pn not in depend_tree["pn"]:
  776. depend_tree["pn"][pn] = {}
  777. depend_tree["pn"][pn]["filename"] = taskfn
  778. version = "%s:%s-%s" % self.recipecaches[mc].pkg_pepvpr[taskfn]
  779. depend_tree["pn"][pn]["version"] = version
  780. rdepends = self.recipecaches[mc].rundeps[taskfn]
  781. rrecs = self.recipecaches[mc].runrecs[taskfn]
  782. depend_tree["pn"][pn]["inherits"] = self.recipecaches[mc].inherits.get(taskfn, None)
  783. # for all extra attributes stored, add them to the dependency tree
  784. for ei in extra_info:
  785. depend_tree["pn"][pn][ei] = vars(self.recipecaches[mc])[ei][taskfn]
  786. if taskfn not in seen_fns:
  787. seen_fns.append(taskfn)
  788. depend_tree["depends"][pn] = []
  789. for dep in taskdata[mc].depids[taskfn]:
  790. pn_provider = ""
  791. if dep in taskdata[mc].build_targets and taskdata[mc].build_targets[dep]:
  792. fn_provider = taskdata[mc].build_targets[dep][0]
  793. pn_provider = self.recipecaches[mc].pkg_fn[fn_provider]
  794. else:
  795. pn_provider = dep
  796. pn_provider = self.add_mc_prefix(mc, pn_provider)
  797. depend_tree["depends"][pn].append(pn_provider)
  798. depend_tree["rdepends-pn"][pn] = []
  799. for rdep in taskdata[mc].rdepids[taskfn]:
  800. pn_rprovider = ""
  801. if rdep in taskdata[mc].run_targets and taskdata[mc].run_targets[rdep]:
  802. fn_rprovider = taskdata[mc].run_targets[rdep][0]
  803. pn_rprovider = self.recipecaches[mc].pkg_fn[fn_rprovider]
  804. else:
  805. pn_rprovider = rdep
  806. pn_rprovider = self.add_mc_prefix(mc, pn_rprovider)
  807. depend_tree["rdepends-pn"][pn].append(pn_rprovider)
  808. depend_tree["rdepends-pkg"].update(rdepends)
  809. depend_tree["rrecs-pkg"].update(rrecs)
  810. return depend_tree
  811. def generateDepTreeEvent(self, pkgs_to_build, task):
  812. """
  813. Create a task dependency graph of pkgs_to_build.
  814. Generate an event with the result
  815. """
  816. depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
  817. bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.data)
  818. def generateDotGraphFiles(self, pkgs_to_build, task):
  819. """
  820. Create a task dependency graph of pkgs_to_build.
  821. Save the result to a set of .dot files.
  822. """
  823. depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
  824. with open('pn-buildlist', 'w') as f:
  825. for pn in depgraph["pn"]:
  826. f.write(pn + "\n")
  827. logger.info("PN build list saved to 'pn-buildlist'")
  828. # Remove old format output files to ensure no confusion with stale data
  829. try:
  830. os.unlink('pn-depends.dot')
  831. except FileNotFoundError:
  832. pass
  833. try:
  834. os.unlink('package-depends.dot')
  835. except FileNotFoundError:
  836. pass
  837. try:
  838. os.unlink('recipe-depends.dot')
  839. except FileNotFoundError:
  840. pass
  841. with open('task-depends.dot', 'w') as f:
  842. f.write("digraph depends {\n")
  843. for task in sorted(depgraph["tdepends"]):
  844. (pn, taskname) = task.rsplit(".", 1)
  845. fn = depgraph["pn"][pn]["filename"]
  846. version = depgraph["pn"][pn]["version"]
  847. f.write('"%s.%s" [label="%s %s\\n%s\\n%s"]\n' % (pn, taskname, pn, taskname, version, fn))
  848. for dep in sorted(depgraph["tdepends"][task]):
  849. f.write('"%s" -> "%s"\n' % (task, dep))
  850. f.write("}\n")
  851. logger.info("Task dependencies saved to 'task-depends.dot'")
  852. def show_appends_with_no_recipes(self):
  853. appends_without_recipes = {}
  854. # Determine which bbappends haven't been applied
  855. for mc in self.multiconfigs:
  856. # First get list of recipes, including skipped
  857. recipefns = list(self.recipecaches[mc].pkg_fn.keys())
  858. recipefns.extend(self.skiplist.keys())
  859. # Work out list of bbappends that have been applied
  860. applied_appends = []
  861. for fn in recipefns:
  862. applied_appends.extend(self.collections[mc].get_file_appends(fn))
  863. appends_without_recipes[mc] = []
  864. for _, appendfn in self.collections[mc].bbappends:
  865. if not appendfn in applied_appends:
  866. appends_without_recipes[mc].append(appendfn)
  867. msgs = []
  868. for mc in sorted(appends_without_recipes.keys()):
  869. if appends_without_recipes[mc]:
  870. msgs.append('No recipes in %s available for:\n %s' % (mc if mc else 'default',
  871. '\n '.join(appends_without_recipes[mc])))
  872. if msgs:
  873. msg = "\n".join(msgs)
  874. warn_only = self.databuilder.mcdata[mc].getVar("BB_DANGLINGAPPENDS_WARNONLY", \
  875. False) or "no"
  876. if warn_only.lower() in ("1", "yes", "true"):
  877. bb.warn(msg)
  878. else:
  879. bb.fatal(msg)
  880. def handlePrefProviders(self):
  881. for mc in self.multiconfigs:
  882. localdata = data.createCopy(self.databuilder.mcdata[mc])
  883. bb.data.expandKeys(localdata)
  884. # Handle PREFERRED_PROVIDERS
  885. for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
  886. try:
  887. (providee, provider) = p.split(':')
  888. except:
  889. providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
  890. continue
  891. if providee in self.recipecaches[mc].preferred and self.recipecaches[mc].preferred[providee] != provider:
  892. providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.recipecaches[mc].preferred[providee])
  893. self.recipecaches[mc].preferred[providee] = provider
  894. def findConfigFilePath(self, configfile):
  895. """
  896. Find the location on disk of configfile and if it exists and was parsed by BitBake
  897. emit the ConfigFilePathFound event with the path to the file.
  898. """
  899. path = bb.cookerdata.findConfigFile(configfile, self.data)
  900. if not path:
  901. return
  902. # Generate a list of parsed configuration files by searching the files
  903. # listed in the __depends and __base_depends variables with a .conf suffix.
  904. conffiles = []
  905. dep_files = self.data.getVar('__base_depends', False) or []
  906. dep_files = dep_files + (self.data.getVar('__depends', False) or [])
  907. for f in dep_files:
  908. if f[0].endswith(".conf"):
  909. conffiles.append(f[0])
  910. _, conf, conffile = path.rpartition("conf/")
  911. match = os.path.join(conf, conffile)
  912. # Try and find matches for conf/conffilename.conf as we don't always
  913. # have the full path to the file.
  914. for cfg in conffiles:
  915. if cfg.endswith(match):
  916. bb.event.fire(bb.event.ConfigFilePathFound(path),
  917. self.data)
  918. break
  919. def findFilesMatchingInDir(self, filepattern, directory):
  920. """
  921. Searches for files containing the substring 'filepattern' which are children of
  922. 'directory' in each BBPATH. i.e. to find all rootfs package classes available
  923. to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes')
  924. or to find all machine configuration files one could call:
  925. findFilesMatchingInDir(self, '.conf', 'conf/machine')
  926. """
  927. matches = []
  928. bbpaths = self.data.getVar('BBPATH').split(':')
  929. for path in bbpaths:
  930. dirpath = os.path.join(path, directory)
  931. if os.path.exists(dirpath):
  932. for root, dirs, files in os.walk(dirpath):
  933. for f in files:
  934. if filepattern in f:
  935. matches.append(f)
  936. if matches:
  937. bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
  938. def testCookerCommandEvent(self, filepattern):
  939. # Dummy command used by OEQA selftest to test tinfoil without IO
  940. matches = ["A", "B"]
  941. bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
  942. def findProviders(self, mc=''):
  943. return bb.providers.findProviders(self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
  944. def findBestProvider(self, pn, mc=''):
  945. if pn in self.recipecaches[mc].providers:
  946. filenames = self.recipecaches[mc].providers[pn]
  947. eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.databuilder.mcdata[mc], self.recipecaches[mc])
  948. if eligible is not None:
  949. filename = eligible[0]
  950. else:
  951. filename = None
  952. return None, None, None, filename
  953. elif pn in self.recipecaches[mc].pkg_pn:
  954. (latest, latest_f, preferred_ver, preferred_file, required) = bb.providers.findBestProvider(pn, self.databuilder.mcdata[mc], self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
  955. if required and preferred_file is None:
  956. return None, None, None, None
  957. return (latest, latest_f, preferred_ver, preferred_file)
  958. else:
  959. return None, None, None, None
  960. def findConfigFiles(self, varname):
  961. """
  962. Find config files which are appropriate values for varname.
  963. i.e. MACHINE, DISTRO
  964. """
  965. possible = []
  966. var = varname.lower()
  967. data = self.data
  968. # iterate configs
  969. bbpaths = data.getVar('BBPATH').split(':')
  970. for path in bbpaths:
  971. confpath = os.path.join(path, "conf", var)
  972. if os.path.exists(confpath):
  973. for root, dirs, files in os.walk(confpath):
  974. # get all child files, these are appropriate values
  975. for f in files:
  976. val, sep, end = f.rpartition('.')
  977. if end == 'conf':
  978. possible.append(val)
  979. if possible:
  980. bb.event.fire(bb.event.ConfigFilesFound(var, possible), self.data)
  981. def findInheritsClass(self, klass):
  982. """
  983. Find all recipes which inherit the specified class
  984. """
  985. pkg_list = []
  986. for pfn in self.recipecaches[''].pkg_fn:
  987. inherits = self.recipecaches[''].inherits.get(pfn, None)
  988. if inherits and klass in inherits:
  989. pkg_list.append(self.recipecaches[''].pkg_fn[pfn])
  990. return pkg_list
  991. def generateTargetsTree(self, klass=None, pkgs=None):
  992. """
  993. Generate a dependency tree of buildable targets
  994. Generate an event with the result
  995. """
  996. # if the caller hasn't specified a pkgs list default to universe
  997. if not pkgs:
  998. pkgs = ['universe']
  999. # if inherited_class passed ensure all recipes which inherit the
  1000. # specified class are included in pkgs
  1001. if klass:
  1002. extra_pkgs = self.findInheritsClass(klass)
  1003. pkgs = pkgs + extra_pkgs
  1004. # generate a dependency tree for all our packages
  1005. tree = self.generatePkgDepTreeData(pkgs, 'build')
  1006. bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.data)
  1007. def interactiveMode( self ):
  1008. """Drop off into a shell"""
  1009. try:
  1010. from bb import shell
  1011. except ImportError:
  1012. parselog.exception("Interactive mode not available")
  1013. raise bb.BBHandledException()
  1014. else:
  1015. shell.start( self )
  1016. def handleCollections(self, collections):
  1017. """Handle collections"""
  1018. errors = False
  1019. self.bbfile_config_priorities = []
  1020. if collections:
  1021. collection_priorities = {}
  1022. collection_depends = {}
  1023. collection_list = collections.split()
  1024. min_prio = 0
  1025. for c in collection_list:
  1026. bb.debug(1,'Processing %s in collection list' % (c))
  1027. # Get collection priority if defined explicitly
  1028. priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
  1029. if priority:
  1030. try:
  1031. prio = int(priority)
  1032. except ValueError:
  1033. parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
  1034. errors = True
  1035. if min_prio == 0 or prio < min_prio:
  1036. min_prio = prio
  1037. collection_priorities[c] = prio
  1038. else:
  1039. collection_priorities[c] = None
  1040. # Check dependencies and store information for priority calculation
  1041. deps = self.data.getVar("LAYERDEPENDS_%s" % c)
  1042. if deps:
  1043. try:
  1044. depDict = bb.utils.explode_dep_versions2(deps)
  1045. except bb.utils.VersionStringException as vse:
  1046. bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
  1047. for dep, oplist in list(depDict.items()):
  1048. if dep in collection_list:
  1049. for opstr in oplist:
  1050. layerver = self.data.getVar("LAYERVERSION_%s" % dep)
  1051. (op, depver) = opstr.split()
  1052. if layerver:
  1053. try:
  1054. res = bb.utils.vercmp_string_op(layerver, depver, op)
  1055. except bb.utils.VersionStringException as vse:
  1056. bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
  1057. if not res:
  1058. 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)
  1059. errors = True
  1060. else:
  1061. 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)
  1062. errors = True
  1063. else:
  1064. parselog.error("Layer '%s' depends on layer '%s', but this layer is not enabled in your configuration", c, dep)
  1065. errors = True
  1066. collection_depends[c] = list(depDict.keys())
  1067. else:
  1068. collection_depends[c] = []
  1069. # Check recommends and store information for priority calculation
  1070. recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
  1071. if recs:
  1072. try:
  1073. recDict = bb.utils.explode_dep_versions2(recs)
  1074. except bb.utils.VersionStringException as vse:
  1075. bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
  1076. for rec, oplist in list(recDict.items()):
  1077. if rec in collection_list:
  1078. if oplist:
  1079. opstr = oplist[0]
  1080. layerver = self.data.getVar("LAYERVERSION_%s" % rec)
  1081. if layerver:
  1082. (op, recver) = opstr.split()
  1083. try:
  1084. res = bb.utils.vercmp_string_op(layerver, recver, op)
  1085. except bb.utils.VersionStringException as vse:
  1086. bb.fatal('Error parsing LAYERRECOMMENDS_%s: %s' % (c, str(vse)))
  1087. if not res:
  1088. 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)
  1089. continue
  1090. else:
  1091. 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)
  1092. continue
  1093. parselog.debug(3,"Layer '%s' recommends layer '%s', so we are adding it", c, rec)
  1094. collection_depends[c].append(rec)
  1095. else:
  1096. parselog.debug(3,"Layer '%s' recommends layer '%s', but this layer is not enabled in your configuration", c, rec)
  1097. # Recursively work out collection priorities based on dependencies
  1098. def calc_layer_priority(collection):
  1099. if not collection_priorities[collection]:
  1100. max_depprio = min_prio
  1101. for dep in collection_depends[collection]:
  1102. calc_layer_priority(dep)
  1103. depprio = collection_priorities[dep]
  1104. if depprio > max_depprio:
  1105. max_depprio = depprio
  1106. max_depprio += 1
  1107. parselog.debug(1, "Calculated priority of layer %s as %d", collection, max_depprio)
  1108. collection_priorities[collection] = max_depprio
  1109. # Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
  1110. for c in collection_list:
  1111. calc_layer_priority(c)
  1112. regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
  1113. if regex is None:
  1114. parselog.error("BBFILE_PATTERN_%s not defined" % c)
  1115. errors = True
  1116. continue
  1117. elif regex == "":
  1118. parselog.debug(1, "BBFILE_PATTERN_%s is empty" % c)
  1119. cre = re.compile('^NULL$')
  1120. errors = False
  1121. else:
  1122. try:
  1123. cre = re.compile(regex)
  1124. except re.error:
  1125. parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
  1126. errors = True
  1127. continue
  1128. self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
  1129. if errors:
  1130. # We've already printed the actual error(s)
  1131. raise CollectionError("Errors during parsing layer configuration")
  1132. def buildSetVars(self):
  1133. """
  1134. Setup any variables needed before starting a build
  1135. """
  1136. t = time.gmtime()
  1137. for mc in self.databuilder.mcdata:
  1138. ds = self.databuilder.mcdata[mc]
  1139. if not ds.getVar("BUILDNAME", False):
  1140. ds.setVar("BUILDNAME", "${DATE}${TIME}")
  1141. ds.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S', t))
  1142. ds.setVar("DATE", time.strftime('%Y%m%d', t))
  1143. ds.setVar("TIME", time.strftime('%H%M%S', t))
  1144. def reset_mtime_caches(self):
  1145. """
  1146. Reset mtime caches - this is particularly important when memory resident as something
  1147. which is cached is not unlikely to have changed since the last invocation (e.g. a
  1148. file associated with a recipe might have been modified by the user).
  1149. """
  1150. build.reset_cache()
  1151. bb.fetch._checksum_cache.mtime_cache.clear()
  1152. siggen_cache = getattr(bb.parse.siggen, 'checksum_cache', None)
  1153. if siggen_cache:
  1154. bb.parse.siggen.checksum_cache.mtime_cache.clear()
  1155. def matchFiles(self, bf, mc=''):
  1156. """
  1157. Find the .bb files which match the expression in 'buildfile'.
  1158. """
  1159. if bf.startswith("/") or bf.startswith("../"):
  1160. bf = os.path.abspath(bf)
  1161. self.collections = {mc: CookerCollectFiles(self.bbfile_config_priorities, mc)}
  1162. filelist, masked, searchdirs = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
  1163. try:
  1164. os.stat(bf)
  1165. bf = os.path.abspath(bf)
  1166. return [bf]
  1167. except OSError:
  1168. regexp = re.compile(bf)
  1169. matches = []
  1170. for f in filelist:
  1171. if regexp.search(f) and os.path.isfile(f):
  1172. matches.append(f)
  1173. return matches
  1174. def matchFile(self, buildfile, mc=''):
  1175. """
  1176. Find the .bb file which matches the expression in 'buildfile'.
  1177. Raise an error if multiple files
  1178. """
  1179. matches = self.matchFiles(buildfile, mc)
  1180. if len(matches) != 1:
  1181. if matches:
  1182. msg = "Unable to match '%s' to a specific recipe file - %s matches found:" % (buildfile, len(matches))
  1183. if matches:
  1184. for f in matches:
  1185. msg += "\n %s" % f
  1186. parselog.error(msg)
  1187. else:
  1188. parselog.error("Unable to find any recipe file matching '%s'" % buildfile)
  1189. raise NoSpecificMatch
  1190. return matches[0]
  1191. def buildFile(self, buildfile, task):
  1192. """
  1193. Build the file matching regexp buildfile
  1194. """
  1195. bb.event.fire(bb.event.BuildInit(), self.data)
  1196. # Too many people use -b because they think it's how you normally
  1197. # specify a target to be built, so show a warning
  1198. bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
  1199. self.buildFileInternal(buildfile, task)
  1200. def buildFileInternal(self, buildfile, task, fireevents=True, quietlog=False):
  1201. """
  1202. Build the file matching regexp buildfile
  1203. """
  1204. # Parse the configuration here. We need to do it explicitly here since
  1205. # buildFile() doesn't use the cache
  1206. self.parseConfiguration()
  1207. # If we are told to do the None task then query the default task
  1208. if task is None:
  1209. task = self.configuration.cmd
  1210. if not task.startswith("do_"):
  1211. task = "do_%s" % task
  1212. fn, cls, mc = bb.cache.virtualfn2realfn(buildfile)
  1213. fn = self.matchFile(fn, mc)
  1214. self.buildSetVars()
  1215. self.reset_mtime_caches()
  1216. bb_caches = bb.cache.MulticonfigCache(self.databuilder, self.data_hash, self.caches_array)
  1217. infos = bb_caches[mc].parse(fn, self.collections[mc].get_file_appends(fn))
  1218. infos = dict(infos)
  1219. fn = bb.cache.realfn2virtual(fn, cls, mc)
  1220. try:
  1221. info_array = infos[fn]
  1222. except KeyError:
  1223. bb.fatal("%s does not exist" % fn)
  1224. if info_array[0].skipped:
  1225. bb.fatal("%s was skipped: %s" % (fn, info_array[0].skipreason))
  1226. self.recipecaches[mc].add_from_recipeinfo(fn, info_array)
  1227. # Tweak some variables
  1228. item = info_array[0].pn
  1229. self.recipecaches[mc].ignored_dependencies = set()
  1230. self.recipecaches[mc].bbfile_priority[fn] = 1
  1231. self.configuration.limited_deps = True
  1232. # Remove external dependencies
  1233. self.recipecaches[mc].task_deps[fn]['depends'] = {}
  1234. self.recipecaches[mc].deps[fn] = []
  1235. self.recipecaches[mc].rundeps[fn] = defaultdict(list)
  1236. self.recipecaches[mc].runrecs[fn] = defaultdict(list)
  1237. # Invalidate task for target if force mode active
  1238. if self.configuration.force:
  1239. logger.verbose("Invalidate task %s, %s", task, fn)
  1240. bb.parse.siggen.invalidate_task(task, self.recipecaches[mc], fn)
  1241. # Setup taskdata structure
  1242. taskdata = {}
  1243. taskdata[mc] = bb.taskdata.TaskData(self.configuration.halt)
  1244. taskdata[mc].add_provider(self.databuilder.mcdata[mc], self.recipecaches[mc], item)
  1245. if quietlog:
  1246. rqloglevel = bb.runqueue.logger.getEffectiveLevel()
  1247. bb.runqueue.logger.setLevel(logging.WARNING)
  1248. buildname = self.databuilder.mcdata[mc].getVar("BUILDNAME")
  1249. if fireevents:
  1250. bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.databuilder.mcdata[mc])
  1251. # Execute the runqueue
  1252. runlist = [[mc, item, task, fn]]
  1253. rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
  1254. def buildFileIdle(server, rq, halt):
  1255. msg = None
  1256. interrupted = 0
  1257. if halt or self.state == state.forceshutdown:
  1258. rq.finish_runqueue(True)
  1259. msg = "Forced shutdown"
  1260. interrupted = 2
  1261. elif self.state == state.shutdown:
  1262. rq.finish_runqueue(False)
  1263. msg = "Stopped build"
  1264. interrupted = 1
  1265. failures = 0
  1266. try:
  1267. retval = rq.execute_runqueue()
  1268. except runqueue.TaskFailure as exc:
  1269. failures += len(exc.args)
  1270. retval = False
  1271. except SystemExit as exc:
  1272. self.command.finishAsyncCommand(str(exc))
  1273. if quietlog:
  1274. bb.runqueue.logger.setLevel(rqloglevel)
  1275. return False
  1276. if not retval:
  1277. if fireevents:
  1278. bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, item, failures, interrupted), self.databuilder.mcdata[mc])
  1279. self.command.finishAsyncCommand(msg)
  1280. # We trashed self.recipecaches above
  1281. self.parsecache_valid = False
  1282. self.configuration.limited_deps = False
  1283. bb.parse.siggen.reset(self.data)
  1284. if quietlog:
  1285. bb.runqueue.logger.setLevel(rqloglevel)
  1286. return False
  1287. if retval is True:
  1288. return True
  1289. return retval
  1290. self.idleCallBackRegister(buildFileIdle, rq)
  1291. def buildTargets(self, targets, task):
  1292. """
  1293. Attempt to build the targets specified
  1294. """
  1295. def buildTargetsIdle(server, rq, halt):
  1296. msg = None
  1297. interrupted = 0
  1298. if halt or self.state == state.forceshutdown:
  1299. rq.finish_runqueue(True)
  1300. msg = "Forced shutdown"
  1301. interrupted = 2
  1302. elif self.state == state.shutdown:
  1303. rq.finish_runqueue(False)
  1304. msg = "Stopped build"
  1305. interrupted = 1
  1306. failures = 0
  1307. try:
  1308. retval = rq.execute_runqueue()
  1309. except runqueue.TaskFailure as exc:
  1310. failures += len(exc.args)
  1311. retval = False
  1312. except SystemExit as exc:
  1313. self.command.finishAsyncCommand(str(exc))
  1314. return False
  1315. if not retval:
  1316. try:
  1317. for mc in self.multiconfigs:
  1318. bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, targets, failures, interrupted), self.databuilder.mcdata[mc])
  1319. finally:
  1320. self.command.finishAsyncCommand(msg)
  1321. return False
  1322. if retval is True:
  1323. return True
  1324. return retval
  1325. self.reset_mtime_caches()
  1326. self.buildSetVars()
  1327. # If we are told to do the None task then query the default task
  1328. if task is None:
  1329. task = self.configuration.cmd
  1330. if not task.startswith("do_"):
  1331. task = "do_%s" % task
  1332. packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
  1333. bb.event.fire(bb.event.BuildInit(packages), self.data)
  1334. taskdata, runlist = self.buildTaskData(targets, task, self.configuration.halt)
  1335. buildname = self.data.getVar("BUILDNAME", False)
  1336. # make targets to always look as <target>:do_<task>
  1337. ntargets = []
  1338. for target in runlist:
  1339. if target[0]:
  1340. ntargets.append("mc:%s:%s:%s" % (target[0], target[1], target[2]))
  1341. ntargets.append("%s:%s" % (target[1], target[2]))
  1342. for mc in self.multiconfigs:
  1343. bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.databuilder.mcdata[mc])
  1344. rq = bb.runqueue.RunQueue(self, self.data, self.recipecaches, taskdata, runlist)
  1345. if 'universe' in targets:
  1346. rq.rqdata.warn_multi_bb = True
  1347. self.idleCallBackRegister(buildTargetsIdle, rq)
  1348. def getAllKeysWithFlags(self, flaglist):
  1349. dump = {}
  1350. for k in self.data.keys():
  1351. try:
  1352. expand = True
  1353. flags = self.data.getVarFlags(k)
  1354. if flags and "func" in flags and "python" in flags:
  1355. expand = False
  1356. v = self.data.getVar(k, expand)
  1357. if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
  1358. dump[k] = {
  1359. 'v' : str(v) ,
  1360. 'history' : self.data.varhistory.variable(k),
  1361. }
  1362. for d in flaglist:
  1363. if flags and d in flags:
  1364. dump[k][d] = flags[d]
  1365. else:
  1366. dump[k][d] = None
  1367. except Exception as e:
  1368. print(e)
  1369. return dump
  1370. def updateCacheSync(self):
  1371. if self.state == state.running:
  1372. return
  1373. # reload files for which we got notifications
  1374. for p in self.inotify_modified_files:
  1375. bb.parse.update_cache(p)
  1376. if p in bb.parse.BBHandler.cached_statements:
  1377. del bb.parse.BBHandler.cached_statements[p]
  1378. self.inotify_modified_files = []
  1379. if not self.baseconfig_valid:
  1380. logger.debug("Reloading base configuration data")
  1381. self.initConfigurationData()
  1382. self.handlePRServ()
  1383. # This is called for all async commands when self.state != running
  1384. def updateCache(self):
  1385. if self.state == state.running:
  1386. return
  1387. if self.state in (state.shutdown, state.forceshutdown, state.error):
  1388. if hasattr(self.parser, 'shutdown'):
  1389. self.parser.shutdown(clean=False)
  1390. self.parser.final_cleanup()
  1391. raise bb.BBHandledException()
  1392. if self.state != state.parsing:
  1393. self.updateCacheSync()
  1394. if self.state != state.parsing and not self.parsecache_valid:
  1395. self.setupParserWatcher()
  1396. bb.parse.siggen.reset(self.data)
  1397. self.parseConfiguration ()
  1398. if CookerFeatures.SEND_SANITYEVENTS in self.featureset:
  1399. for mc in self.multiconfigs:
  1400. bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
  1401. for mc in self.multiconfigs:
  1402. ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
  1403. self.recipecaches[mc].ignored_dependencies = set(ignore.split())
  1404. for dep in self.configuration.extra_assume_provided:
  1405. self.recipecaches[mc].ignored_dependencies.add(dep)
  1406. self.collections = {}
  1407. mcfilelist = {}
  1408. total_masked = 0
  1409. searchdirs = set()
  1410. for mc in self.multiconfigs:
  1411. self.collections[mc] = CookerCollectFiles(self.bbfile_config_priorities, mc)
  1412. (filelist, masked, search) = self.collections[mc].collect_bbfiles(self.databuilder.mcdata[mc], self.databuilder.mcdata[mc])
  1413. mcfilelist[mc] = filelist
  1414. total_masked += masked
  1415. searchdirs |= set(search)
  1416. # Add inotify watches for directories searched for bb/bbappend files
  1417. for dirent in searchdirs:
  1418. self.add_filewatch([[dirent]], dirs=True)
  1419. self.parser = CookerParser(self, mcfilelist, total_masked)
  1420. self.parsecache_valid = True
  1421. self.state = state.parsing
  1422. if not self.parser.parse_next():
  1423. collectlog.debug(1, "parsing complete")
  1424. if self.parser.error:
  1425. raise bb.BBHandledException()
  1426. self.show_appends_with_no_recipes()
  1427. self.handlePrefProviders()
  1428. for mc in self.multiconfigs:
  1429. self.recipecaches[mc].bbfile_priority = self.collections[mc].collection_priorities(self.recipecaches[mc].pkg_fn, self.parser.mcfilelist[mc], self.data)
  1430. self.state = state.running
  1431. # Send an event listing all stamps reachable after parsing
  1432. # which the metadata may use to clean up stale data
  1433. for mc in self.multiconfigs:
  1434. event = bb.event.ReachableStamps(self.recipecaches[mc].stamp)
  1435. bb.event.fire(event, self.databuilder.mcdata[mc])
  1436. return None
  1437. return True
  1438. def checkPackages(self, pkgs_to_build, task=None):
  1439. # Return a copy, don't modify the original
  1440. pkgs_to_build = pkgs_to_build[:]
  1441. if not pkgs_to_build:
  1442. raise NothingToBuild
  1443. ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
  1444. for pkg in pkgs_to_build.copy():
  1445. if pkg in ignore:
  1446. parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
  1447. if pkg.startswith("multiconfig:"):
  1448. pkgs_to_build.remove(pkg)
  1449. pkgs_to_build.append(pkg.replace("multiconfig:", "mc:"))
  1450. if 'world' in pkgs_to_build:
  1451. pkgs_to_build.remove('world')
  1452. for mc in self.multiconfigs:
  1453. bb.providers.buildWorldTargetList(self.recipecaches[mc], task)
  1454. for t in self.recipecaches[mc].world_target:
  1455. if mc:
  1456. t = "mc:" + mc + ":" + t
  1457. pkgs_to_build.append(t)
  1458. if 'universe' in pkgs_to_build:
  1459. parselog.verbnote("The \"universe\" target is only intended for testing and may produce errors.")
  1460. parselog.debug(1, "collating packages for \"universe\"")
  1461. pkgs_to_build.remove('universe')
  1462. for mc in self.multiconfigs:
  1463. for t in self.recipecaches[mc].universe_target:
  1464. if task:
  1465. foundtask = False
  1466. for provider_fn in self.recipecaches[mc].providers[t]:
  1467. if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
  1468. foundtask = True
  1469. break
  1470. if not foundtask:
  1471. bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
  1472. continue
  1473. if mc:
  1474. t = "mc:" + mc + ":" + t
  1475. pkgs_to_build.append(t)
  1476. return pkgs_to_build
  1477. def pre_serve(self):
  1478. return
  1479. def post_serve(self):
  1480. self.shutdown(force=True)
  1481. prserv.serv.auto_shutdown()
  1482. if hasattr(bb.parse, "siggen"):
  1483. bb.parse.siggen.exit()
  1484. if self.hashserv:
  1485. self.hashserv.process.terminate()
  1486. self.hashserv.process.join()
  1487. if hasattr(self, "data"):
  1488. bb.event.fire(CookerExit(), self.data)
  1489. def shutdown(self, force = False):
  1490. if force:
  1491. self.state = state.forceshutdown
  1492. else:
  1493. self.state = state.shutdown
  1494. if self.parser:
  1495. self.parser.shutdown(clean=not force)
  1496. self.parser.final_cleanup()
  1497. def finishcommand(self):
  1498. self.state = state.initial
  1499. def reset(self):
  1500. if hasattr(bb.parse, "siggen"):
  1501. bb.parse.siggen.exit()
  1502. self.initConfigurationData()
  1503. self.handlePRServ()
  1504. def clientComplete(self):
  1505. """Called when the client is done using the server"""
  1506. self.finishcommand()
  1507. self.extraconfigdata = {}
  1508. self.command.reset()
  1509. if hasattr(self, "data"):
  1510. self.databuilder.reset()
  1511. self.data = self.databuilder.data
  1512. self.parsecache_valid = False
  1513. self.baseconfig_valid = False
  1514. class CookerExit(bb.event.Event):
  1515. """
  1516. Notify clients of the Cooker shutdown
  1517. """
  1518. def __init__(self):
  1519. bb.event.Event.__init__(self)
  1520. class CookerCollectFiles(object):
  1521. def __init__(self, priorities, mc=''):
  1522. self.mc = mc
  1523. self.bbappends = []
  1524. # Priorities is a list of tuples, with the second element as the pattern.
  1525. # We need to sort the list with the longest pattern first, and so on to
  1526. # the shortest. This allows nested layers to be properly evaluated.
  1527. self.bbfile_config_priorities = sorted(priorities, key=lambda tup: tup[1], reverse=True)
  1528. def calc_bbfile_priority(self, filename):
  1529. for _, _, regex, pri in self.bbfile_config_priorities:
  1530. if regex.match(filename):
  1531. return pri, regex
  1532. return 0, None
  1533. def get_bbfiles(self):
  1534. """Get list of default .bb files by reading out the current directory"""
  1535. path = os.getcwd()
  1536. contents = os.listdir(path)
  1537. bbfiles = []
  1538. for f in contents:
  1539. if f.endswith(".bb"):
  1540. bbfiles.append(os.path.abspath(os.path.join(path, f)))
  1541. return bbfiles
  1542. def find_bbfiles(self, path):
  1543. """Find all the .bb and .bbappend files in a directory"""
  1544. found = []
  1545. for dir, dirs, files in os.walk(path):
  1546. for ignored in ('SCCS', 'CVS', '.svn'):
  1547. if ignored in dirs:
  1548. dirs.remove(ignored)
  1549. found += [os.path.join(dir, f) for f in files if (f.endswith(['.bb', '.bbappend']))]
  1550. return found
  1551. def collect_bbfiles(self, config, eventdata):
  1552. """Collect all available .bb build files"""
  1553. masked = 0
  1554. collectlog.debug(1, "collecting .bb files")
  1555. files = (config.getVar( "BBFILES") or "").split()
  1556. # Sort files by priority
  1557. files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem)[0] )
  1558. config.setVar("BBFILES_PRIORITIZED", " ".join(files))
  1559. if not files:
  1560. files = self.get_bbfiles()
  1561. if not files:
  1562. collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
  1563. bb.event.fire(CookerExit(), eventdata)
  1564. # We need to track where we look so that we can add inotify watches. There
  1565. # is no nice way to do this, this is horrid. We intercept the os.listdir()
  1566. # (or os.scandir() for python 3.6+) calls while we run glob().
  1567. origlistdir = os.listdir
  1568. if hasattr(os, 'scandir'):
  1569. origscandir = os.scandir
  1570. searchdirs = []
  1571. def ourlistdir(d):
  1572. searchdirs.append(d)
  1573. return origlistdir(d)
  1574. def ourscandir(d):
  1575. searchdirs.append(d)
  1576. return origscandir(d)
  1577. os.listdir = ourlistdir
  1578. if hasattr(os, 'scandir'):
  1579. os.scandir = ourscandir
  1580. try:
  1581. # Can't use set here as order is important
  1582. newfiles = []
  1583. for f in files:
  1584. if os.path.isdir(f):
  1585. dirfiles = self.find_bbfiles(f)
  1586. for g in dirfiles:
  1587. if g not in newfiles:
  1588. newfiles.append(g)
  1589. else:
  1590. globbed = glob.glob(f)
  1591. if not globbed and os.path.exists(f):
  1592. globbed = [f]
  1593. # glob gives files in order on disk. Sort to be deterministic.
  1594. for g in sorted(globbed):
  1595. if g not in newfiles:
  1596. newfiles.append(g)
  1597. finally:
  1598. os.listdir = origlistdir
  1599. if hasattr(os, 'scandir'):
  1600. os.scandir = origscandir
  1601. bbmask = config.getVar('BBMASK')
  1602. if bbmask:
  1603. # First validate the individual regular expressions and ignore any
  1604. # that do not compile
  1605. bbmasks = []
  1606. for mask in bbmask.split():
  1607. # When constructing an older style single regex, it's possible for BBMASK
  1608. # to end up beginning with '|', which matches and masks _everything_.
  1609. if mask.startswith("|"):
  1610. collectlog.warning("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
  1611. mask = mask[1:]
  1612. try:
  1613. re.compile(mask)
  1614. bbmasks.append(mask)
  1615. except re.error:
  1616. collectlog.critical("BBMASK contains an invalid regular expression, ignoring: %s" % mask)
  1617. # Then validate the combined regular expressions. This should never
  1618. # fail, but better safe than sorry...
  1619. bbmask = "|".join(bbmasks)
  1620. try:
  1621. bbmask_compiled = re.compile(bbmask)
  1622. except re.error:
  1623. collectlog.critical("BBMASK is not a valid regular expression, ignoring: %s" % bbmask)
  1624. bbmask = None
  1625. bbfiles = []
  1626. bbappend = []
  1627. for f in newfiles:
  1628. if bbmask and bbmask_compiled.search(f):
  1629. collectlog.debug(1, "skipping masked file %s", f)
  1630. masked += 1
  1631. continue
  1632. if f.endswith('.bb'):
  1633. bbfiles.append(f)
  1634. elif f.endswith('.bbappend'):
  1635. bbappend.append(f)
  1636. else:
  1637. collectlog.debug(1, "skipping %s: unknown file extension", f)
  1638. # Build a list of .bbappend files for each .bb file
  1639. for f in bbappend:
  1640. base = os.path.basename(f).replace('.bbappend', '.bb')
  1641. self.bbappends.append((base, f))
  1642. # Find overlayed recipes
  1643. # bbfiles will be in priority order which makes this easy
  1644. bbfile_seen = dict()
  1645. self.overlayed = defaultdict(list)
  1646. for f in reversed(bbfiles):
  1647. base = os.path.basename(f)
  1648. if base not in bbfile_seen:
  1649. bbfile_seen[base] = f
  1650. else:
  1651. topfile = bbfile_seen[base]
  1652. self.overlayed[topfile].append(f)
  1653. return (bbfiles, masked, searchdirs)
  1654. def get_file_appends(self, fn):
  1655. """
  1656. Returns a list of .bbappend files to apply to fn
  1657. """
  1658. filelist = []
  1659. f = os.path.basename(fn)
  1660. for b in self.bbappends:
  1661. (bbappend, filename) = b
  1662. if (bbappend == f) or ('%' in bbappend and bbappend.startswith(f[:bbappend.index('%')])):
  1663. filelist.append(filename)
  1664. return tuple(filelist)
  1665. def collection_priorities(self, pkgfns, fns, d):
  1666. # Return the priorities of the entries in pkgfns
  1667. # Also check that all the regexes in self.bbfile_config_priorities are used
  1668. # (but to do that we need to ensure skipped recipes aren't counted, nor
  1669. # collections in BBFILE_PATTERN_IGNORE_EMPTY)
  1670. priorities = {}
  1671. seen = set()
  1672. matched = set()
  1673. matched_regex = set()
  1674. unmatched_regex = set()
  1675. for _, _, regex, _ in self.bbfile_config_priorities:
  1676. unmatched_regex.add(regex)
  1677. # Calculate priorities for each file
  1678. for p in pkgfns:
  1679. realfn, cls, mc = bb.cache.virtualfn2realfn(p)
  1680. priorities[p], regex = self.calc_bbfile_priority(realfn)
  1681. if regex in unmatched_regex:
  1682. matched_regex.add(regex)
  1683. unmatched_regex.remove(regex)
  1684. seen.add(realfn)
  1685. if regex:
  1686. matched.add(realfn)
  1687. if unmatched_regex:
  1688. # Account for bbappend files
  1689. for b in self.bbappends:
  1690. (bbfile, append) = b
  1691. seen.add(append)
  1692. # Account for skipped recipes
  1693. seen.update(fns)
  1694. seen.difference_update(matched)
  1695. def already_matched(fn):
  1696. for regex in matched_regex:
  1697. if regex.match(fn):
  1698. return True
  1699. return False
  1700. for unmatch in unmatched_regex.copy():
  1701. for fn in seen:
  1702. if unmatch.match(fn):
  1703. # If the bbappend or file was already matched by another regex, skip it
  1704. # e.g. for a layer within a layer, the outer regex could match, the inner
  1705. # regex may match nothing and we should warn about that
  1706. if already_matched(fn):
  1707. continue
  1708. unmatched_regex.remove(unmatch)
  1709. break
  1710. for collection, pattern, regex, _ in self.bbfile_config_priorities:
  1711. if regex in unmatched_regex:
  1712. if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
  1713. collectlog.warning("No bb files in %s matched BBFILE_PATTERN_%s '%s'" % (self.mc if self.mc else 'default',
  1714. collection, pattern))
  1715. return priorities
  1716. class ParsingFailure(Exception):
  1717. def __init__(self, realexception, recipe):
  1718. self.realexception = realexception
  1719. self.recipe = recipe
  1720. Exception.__init__(self, realexception, recipe)
  1721. class Parser(multiprocessing.Process):
  1722. def __init__(self, jobs, results, quit, profile):
  1723. self.jobs = jobs
  1724. self.results = results
  1725. self.quit = quit
  1726. multiprocessing.Process.__init__(self)
  1727. self.context = bb.utils.get_context().copy()
  1728. self.handlers = bb.event.get_class_handlers().copy()
  1729. self.profile = profile
  1730. self.queue_signals = False
  1731. self.signal_received = []
  1732. self.signal_threadlock = threading.Lock()
  1733. def catch_sig(self, signum, frame):
  1734. if self.queue_signals:
  1735. self.signal_received.append(signum)
  1736. else:
  1737. self.handle_sig(signum, frame)
  1738. def handle_sig(self, signum, frame):
  1739. if signum == signal.SIGTERM:
  1740. signal.signal(signal.SIGTERM, signal.SIG_DFL)
  1741. os.kill(os.getpid(), signal.SIGTERM)
  1742. elif signum == signal.SIGINT:
  1743. signal.default_int_handler(signum, frame)
  1744. def run(self):
  1745. if not self.profile:
  1746. self.realrun()
  1747. return
  1748. try:
  1749. import cProfile as profile
  1750. except:
  1751. import profile
  1752. prof = profile.Profile()
  1753. try:
  1754. profile.Profile.runcall(prof, self.realrun)
  1755. finally:
  1756. logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
  1757. prof.dump_stats(logfile)
  1758. def realrun(self):
  1759. # Signal handling here is hard. We must not terminate any process or thread holding the write
  1760. # lock for the event stream as it will not be released, ever, and things will hang.
  1761. # Python handles signals in the main thread/process but they can be raised from any thread and
  1762. # we want to defer processing of any SIGTERM/SIGINT signal until we're outside the critical section
  1763. # and don't hold the lock (see server/process.py). We therefore always catch the signals (so any
  1764. # new thread should also do so) and we defer handling but we handle with the local thread lock
  1765. # held (a threading lock, not a multiprocessing one) so that no other thread in the process
  1766. # can be in the critical section.
  1767. signal.signal(signal.SIGTERM, self.catch_sig)
  1768. signal.signal(signal.SIGHUP, signal.SIG_DFL)
  1769. signal.signal(signal.SIGINT, self.catch_sig)
  1770. bb.utils.set_process_name(multiprocessing.current_process().name)
  1771. multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, exitpriority=1)
  1772. multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, exitpriority=1)
  1773. pending = []
  1774. try:
  1775. while True:
  1776. try:
  1777. self.quit.get_nowait()
  1778. except queue.Empty:
  1779. pass
  1780. else:
  1781. break
  1782. if pending:
  1783. result = pending.pop()
  1784. else:
  1785. try:
  1786. job = self.jobs.pop()
  1787. except IndexError:
  1788. break
  1789. result = self.parse(*job)
  1790. # Clear the siggen cache after parsing to control memory usage, its huge
  1791. bb.parse.siggen.postparsing_clean_cache()
  1792. try:
  1793. self.results.put(result, timeout=0.25)
  1794. except queue.Full:
  1795. pending.append(result)
  1796. finally:
  1797. self.results.close()
  1798. self.results.join_thread()
  1799. def parse(self, mc, cache, filename, appends):
  1800. try:
  1801. origfilter = bb.event.LogHandler.filter
  1802. # Record the filename we're parsing into any events generated
  1803. def parse_filter(self, record):
  1804. record.taskpid = bb.event.worker_pid
  1805. record.fn = filename
  1806. return True
  1807. # Reset our environment and handlers to the original settings
  1808. bb.utils.set_context(self.context.copy())
  1809. bb.event.set_class_handlers(self.handlers.copy())
  1810. bb.event.LogHandler.filter = parse_filter
  1811. return True, mc, cache.parse(filename, appends)
  1812. except Exception as exc:
  1813. tb = sys.exc_info()[2]
  1814. exc.recipe = filename
  1815. exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
  1816. return True, None, exc
  1817. # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
  1818. # and for example a worker thread doesn't just exit on its own in response to
  1819. # a SystemExit event for example.
  1820. except BaseException as exc:
  1821. return True, None, ParsingFailure(exc, filename)
  1822. finally:
  1823. bb.event.LogHandler.filter = origfilter
  1824. class CookerParser(object):
  1825. def __init__(self, cooker, mcfilelist, masked):
  1826. self.mcfilelist = mcfilelist
  1827. self.cooker = cooker
  1828. self.cfgdata = cooker.data
  1829. self.cfghash = cooker.data_hash
  1830. self.cfgbuilder = cooker.databuilder
  1831. # Accounting statistics
  1832. self.parsed = 0
  1833. self.cached = 0
  1834. self.error = 0
  1835. self.masked = masked
  1836. self.skipped = 0
  1837. self.virtuals = 0
  1838. self.current = 0
  1839. self.process_names = []
  1840. self.bb_caches = bb.cache.MulticonfigCache(self.cfgbuilder, self.cfghash, cooker.caches_array)
  1841. self.fromcache = set()
  1842. self.willparse = set()
  1843. for mc in self.cooker.multiconfigs:
  1844. for filename in self.mcfilelist[mc]:
  1845. appends = self.cooker.collections[mc].get_file_appends(filename)
  1846. if not self.bb_caches[mc].cacheValid(filename, appends):
  1847. self.willparse.add((mc, self.bb_caches[mc], filename, appends))
  1848. else:
  1849. self.fromcache.add((mc, self.bb_caches[mc], filename, appends))
  1850. self.total = len(self.fromcache) + len(self.willparse)
  1851. self.toparse = len(self.willparse)
  1852. self.progress_chunk = int(max(self.toparse / 100, 1))
  1853. self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
  1854. multiprocessing.cpu_count()), self.toparse)
  1855. self.start()
  1856. self.haveshutdown = False
  1857. self.syncthread = None
  1858. def start(self):
  1859. self.results = self.load_cached()
  1860. self.processes = []
  1861. if self.toparse:
  1862. bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
  1863. self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes)
  1864. self.result_queue = multiprocessing.Queue()
  1865. def chunkify(lst,n):
  1866. return [lst[i::n] for i in range(n)]
  1867. self.jobs = chunkify(list(self.willparse), self.num_processes)
  1868. for i in range(0, self.num_processes):
  1869. parser = Parser(self.jobs[i], self.result_queue, self.parser_quit, self.cooker.configuration.profile)
  1870. parser.start()
  1871. self.process_names.append(parser.name)
  1872. self.processes.append(parser)
  1873. self.results = itertools.chain(self.results, self.parse_generator())
  1874. def shutdown(self, clean=True):
  1875. if not self.toparse:
  1876. return
  1877. if self.haveshutdown:
  1878. return
  1879. self.haveshutdown = True
  1880. if clean:
  1881. event = bb.event.ParseCompleted(self.cached, self.parsed,
  1882. self.skipped, self.masked,
  1883. self.virtuals, self.error,
  1884. self.total)
  1885. bb.event.fire(event, self.cfgdata)
  1886. else:
  1887. bb.error("Parsing halted due to errors, see error messages above")
  1888. for process in self.processes:
  1889. self.parser_quit.put(None)
  1890. # Cleanup the queue before call process.join(), otherwise there might be
  1891. # deadlocks.
  1892. while True:
  1893. try:
  1894. self.result_queue.get(timeout=0.25)
  1895. except queue.Empty:
  1896. break
  1897. for process in self.processes:
  1898. process.join(0.5)
  1899. for process in self.processes:
  1900. if process.exitcode is None:
  1901. os.kill(process.pid, signal.SIGINT)
  1902. for process in self.processes:
  1903. process.join(0.5)
  1904. for process in self.processes:
  1905. if process.exitcode is None:
  1906. process.terminate()
  1907. for process in self.processes:
  1908. process.join()
  1909. # Added in 3.7, cleans up zombies
  1910. if hasattr(process, "close"):
  1911. process.close()
  1912. self.parser_quit.close()
  1913. # Allow data left in the cancel queue to be discarded
  1914. self.parser_quit.cancel_join_thread()
  1915. def sync_caches():
  1916. for c in self.bb_caches.values():
  1917. c.sync()
  1918. sync = threading.Thread(target=sync_caches, name="SyncThread")
  1919. self.syncthread = sync
  1920. sync.start()
  1921. bb.codeparser.parser_cache_savemerge()
  1922. bb.fetch.fetcher_parse_done()
  1923. if self.cooker.configuration.profile:
  1924. profiles = []
  1925. for i in self.process_names:
  1926. logfile = "profile-parse-%s.log" % i
  1927. if os.path.exists(logfile):
  1928. profiles.append(logfile)
  1929. pout = "profile-parse.log.processed"
  1930. bb.utils.process_profilelog(profiles, pout = pout)
  1931. print("Processed parsing statistics saved to %s" % (pout))
  1932. def final_cleanup(self):
  1933. if self.syncthread:
  1934. self.syncthread.join()
  1935. def load_cached(self):
  1936. for mc, cache, filename, appends in self.fromcache:
  1937. cached, infos = cache.load(filename, appends)
  1938. yield not cached, mc, infos
  1939. def parse_generator(self):
  1940. empty = False
  1941. while self.processes or not empty:
  1942. for process in self.processes.copy():
  1943. if not process.is_alive():
  1944. process.join()
  1945. self.processes.remove(process)
  1946. if self.parsed >= self.toparse:
  1947. break
  1948. try:
  1949. result = self.result_queue.get(timeout=0.25)
  1950. except queue.Empty:
  1951. empty = True
  1952. yield None, None, None
  1953. else:
  1954. empty = False
  1955. yield result
  1956. if not (self.parsed >= self.toparse):
  1957. raise bb.parse.ParseError("Not all recipes parsed, parser thread killed/died? Exiting.", None)
  1958. def parse_next(self):
  1959. result = []
  1960. parsed = None
  1961. try:
  1962. parsed, mc, result = next(self.results)
  1963. if isinstance(result, BaseException):
  1964. # Turn exceptions back into exceptions
  1965. raise result
  1966. if parsed is None:
  1967. # Timeout, loop back through the main loop
  1968. return True
  1969. except StopIteration:
  1970. self.shutdown()
  1971. return False
  1972. except bb.BBHandledException as exc:
  1973. self.error += 1
  1974. logger.debug('Failed to parse recipe: %s' % exc.recipe)
  1975. self.shutdown(clean=False)
  1976. return False
  1977. except ParsingFailure as exc:
  1978. self.error += 1
  1979. logger.error('Unable to parse %s: %s' %
  1980. (exc.recipe, bb.exceptions.to_string(exc.realexception)))
  1981. self.shutdown(clean=False)
  1982. return False
  1983. except bb.parse.ParseError as exc:
  1984. self.error += 1
  1985. logger.error(str(exc))
  1986. self.shutdown(clean=False)
  1987. return False
  1988. except bb.data_smart.ExpansionError as exc:
  1989. self.error += 1
  1990. bbdir = os.path.dirname(__file__) + os.sep
  1991. etype, value, _ = sys.exc_info()
  1992. tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback))
  1993. logger.error('ExpansionError during parsing %s', value.recipe,
  1994. exc_info=(etype, value, tb))
  1995. self.shutdown(clean=False)
  1996. return False
  1997. except Exception as exc:
  1998. self.error += 1
  1999. etype, value, tb = sys.exc_info()
  2000. if hasattr(value, "recipe"):
  2001. logger.error('Unable to parse %s' % value.recipe,
  2002. exc_info=(etype, value, exc.traceback))
  2003. else:
  2004. # Most likely, an exception occurred during raising an exception
  2005. import traceback
  2006. logger.error('Exception during parse: %s' % traceback.format_exc())
  2007. self.shutdown(clean=False)
  2008. return False
  2009. self.current += 1
  2010. self.virtuals += len(result)
  2011. if parsed:
  2012. self.parsed += 1
  2013. if self.parsed % self.progress_chunk == 0:
  2014. bb.event.fire(bb.event.ParseProgress(self.parsed, self.toparse),
  2015. self.cfgdata)
  2016. else:
  2017. self.cached += 1
  2018. for virtualfn, info_array in result:
  2019. if info_array[0].skipped:
  2020. self.skipped += 1
  2021. self.cooker.skiplist[virtualfn] = SkippedPackage(info_array[0])
  2022. self.bb_caches[mc].add_info(virtualfn, info_array, self.cooker.recipecaches[mc],
  2023. parsed=parsed, watcher = self.cooker.add_filewatch)
  2024. return True
  2025. def reparse(self, filename):
  2026. to_reparse = set()
  2027. for mc in self.cooker.multiconfigs:
  2028. to_reparse.add((mc, filename, self.cooker.collections[mc].get_file_appends(filename)))
  2029. for mc, filename, appends in to_reparse:
  2030. infos = self.bb_caches[mc].parse(filename, appends)
  2031. for vfn, info_array in infos:
  2032. self.cooker.recipecaches[mc].add_from_recipeinfo(vfn, info_array)