cookerdata.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. #!/usr/bin/env python
  2. # ex:ts=4:sw=4:sts=4:et
  3. # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
  4. #
  5. # Copyright (C) 2003, 2004 Chris Larson
  6. # Copyright (C) 2003, 2004 Phil Blundell
  7. # Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
  8. # Copyright (C) 2005 Holger Hans Peter Freyther
  9. # Copyright (C) 2005 ROAD GmbH
  10. # Copyright (C) 2006 Richard Purdie
  11. #
  12. # SPDX-License-Identifier: GPL-2.0-only
  13. #
  14. import logging
  15. import os
  16. import re
  17. import sys
  18. from functools import wraps
  19. import bb
  20. from bb import data
  21. import bb.parse
  22. logger = logging.getLogger("BitBake")
  23. parselog = logging.getLogger("BitBake.Parsing")
  24. class ConfigParameters(object):
  25. def __init__(self, argv=sys.argv):
  26. self.options, targets = self.parseCommandLine(argv)
  27. self.environment = self.parseEnvironment()
  28. self.options.pkgs_to_build = targets or []
  29. for key, val in self.options.__dict__.items():
  30. setattr(self, key, val)
  31. def parseCommandLine(self, argv=sys.argv):
  32. raise Exception("Caller must implement commandline option parsing")
  33. def parseEnvironment(self):
  34. return os.environ.copy()
  35. def updateFromServer(self, server):
  36. if not self.options.cmd:
  37. defaulttask, error = server.runCommand(["getVariable", "BB_DEFAULT_TASK"])
  38. if error:
  39. raise Exception("Unable to get the value of BB_DEFAULT_TASK from the server: %s" % error)
  40. self.options.cmd = defaulttask or "build"
  41. _, error = server.runCommand(["setConfig", "cmd", self.options.cmd])
  42. if error:
  43. raise Exception("Unable to set configuration option 'cmd' on the server: %s" % error)
  44. if not self.options.pkgs_to_build:
  45. bbpkgs, error = server.runCommand(["getVariable", "BBTARGETS"])
  46. if error:
  47. raise Exception("Unable to get the value of BBTARGETS from the server: %s" % error)
  48. if bbpkgs:
  49. self.options.pkgs_to_build.extend(bbpkgs.split())
  50. def updateToServer(self, server, environment):
  51. options = {}
  52. for o in ["abort", "force", "invalidate_stamp",
  53. "verbose", "debug", "dry_run", "dump_signatures",
  54. "debug_domains", "extra_assume_provided", "profile",
  55. "prefile", "postfile", "server_timeout"]:
  56. options[o] = getattr(self.options, o)
  57. ret, error = server.runCommand(["updateConfig", options, environment, sys.argv])
  58. if error:
  59. raise Exception("Unable to update the server configuration with local parameters: %s" % error)
  60. def parseActions(self):
  61. # Parse any commandline into actions
  62. action = {'action':None, 'msg':None}
  63. if self.options.show_environment:
  64. if 'world' in self.options.pkgs_to_build:
  65. action['msg'] = "'world' is not a valid target for --environment."
  66. elif 'universe' in self.options.pkgs_to_build:
  67. action['msg'] = "'universe' is not a valid target for --environment."
  68. elif len(self.options.pkgs_to_build) > 1:
  69. action['msg'] = "Only one target can be used with the --environment option."
  70. elif self.options.buildfile and len(self.options.pkgs_to_build) > 0:
  71. action['msg'] = "No target should be used with the --environment and --buildfile options."
  72. elif len(self.options.pkgs_to_build) > 0:
  73. action['action'] = ["showEnvironmentTarget", self.options.pkgs_to_build]
  74. else:
  75. action['action'] = ["showEnvironment", self.options.buildfile]
  76. elif self.options.buildfile is not None:
  77. action['action'] = ["buildFile", self.options.buildfile, self.options.cmd]
  78. elif self.options.revisions_changed:
  79. action['action'] = ["compareRevisions"]
  80. elif self.options.show_versions:
  81. action['action'] = ["showVersions"]
  82. elif self.options.parse_only:
  83. action['action'] = ["parseFiles"]
  84. elif self.options.dot_graph:
  85. if self.options.pkgs_to_build:
  86. action['action'] = ["generateDotGraph", self.options.pkgs_to_build, self.options.cmd]
  87. else:
  88. action['msg'] = "Please specify a package name for dependency graph generation."
  89. else:
  90. if self.options.pkgs_to_build:
  91. action['action'] = ["buildTargets", self.options.pkgs_to_build, self.options.cmd]
  92. else:
  93. #action['msg'] = "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information."
  94. action = None
  95. self.options.initialaction = action
  96. return action
  97. class CookerConfiguration(object):
  98. """
  99. Manages build options and configurations for one run
  100. """
  101. def __init__(self):
  102. self.debug_domains = []
  103. self.extra_assume_provided = []
  104. self.prefile = []
  105. self.postfile = []
  106. self.debug = 0
  107. self.cmd = None
  108. self.abort = True
  109. self.force = False
  110. self.profile = False
  111. self.nosetscene = False
  112. self.setsceneonly = False
  113. self.invalidate_stamp = False
  114. self.dump_signatures = []
  115. self.dry_run = False
  116. self.tracking = False
  117. self.xmlrpcinterface = []
  118. self.server_timeout = None
  119. self.writeeventlog = False
  120. self.server_only = False
  121. self.limited_deps = False
  122. self.runall = []
  123. self.runonly = []
  124. self.env = {}
  125. def setConfigParameters(self, parameters):
  126. for key in self.__dict__.keys():
  127. if key in parameters.options.__dict__:
  128. setattr(self, key, parameters.options.__dict__[key])
  129. self.env = parameters.environment.copy()
  130. def setServerRegIdleCallback(self, srcb):
  131. self.server_register_idlecallback = srcb
  132. def __getstate__(self):
  133. state = {}
  134. for key in self.__dict__.keys():
  135. if key == "server_register_idlecallback":
  136. state[key] = None
  137. else:
  138. state[key] = getattr(self, key)
  139. return state
  140. def __setstate__(self,state):
  141. for k in state:
  142. setattr(self, k, state[k])
  143. def catch_parse_error(func):
  144. """Exception handling bits for our parsing"""
  145. @wraps(func)
  146. def wrapped(fn, *args):
  147. try:
  148. return func(fn, *args)
  149. except IOError as exc:
  150. import traceback
  151. parselog.critical(traceback.format_exc())
  152. parselog.critical("Unable to parse %s: %s" % (fn, exc))
  153. sys.exit(1)
  154. except bb.data_smart.ExpansionError as exc:
  155. import traceback
  156. bbdir = os.path.dirname(__file__) + os.sep
  157. exc_class, exc, tb = sys.exc_info()
  158. for tb in iter(lambda: tb.tb_next, None):
  159. # Skip frames in bitbake itself, we only want the metadata
  160. fn, _, _, _ = traceback.extract_tb(tb, 1)[0]
  161. if not fn.startswith(bbdir):
  162. break
  163. parselog.critical("Unable to parse %s" % fn, exc_info=(exc_class, exc, tb))
  164. sys.exit(1)
  165. except bb.parse.ParseError as exc:
  166. parselog.critical(str(exc))
  167. sys.exit(1)
  168. return wrapped
  169. @catch_parse_error
  170. def parse_config_file(fn, data, include=True):
  171. return bb.parse.handle(fn, data, include)
  172. @catch_parse_error
  173. def _inherit(bbclass, data):
  174. bb.parse.BBHandler.inherit(bbclass, "configuration INHERITs", 0, data)
  175. return data
  176. def findConfigFile(configfile, data):
  177. search = []
  178. bbpath = data.getVar("BBPATH")
  179. if bbpath:
  180. for i in bbpath.split(":"):
  181. search.append(os.path.join(i, "conf", configfile))
  182. path = os.getcwd()
  183. while path != "/":
  184. search.append(os.path.join(path, "conf", configfile))
  185. path, _ = os.path.split(path)
  186. for i in search:
  187. if os.path.exists(i):
  188. return i
  189. return None
  190. #
  191. # We search for a conf/bblayers.conf under an entry in BBPATH or in cwd working
  192. # up to /. If that fails, we search for a conf/bitbake.conf in BBPATH.
  193. #
  194. def findTopdir():
  195. d = bb.data.init()
  196. bbpath = None
  197. if 'BBPATH' in os.environ:
  198. bbpath = os.environ['BBPATH']
  199. d.setVar('BBPATH', bbpath)
  200. layerconf = findConfigFile("bblayers.conf", d)
  201. if layerconf:
  202. return os.path.dirname(os.path.dirname(layerconf))
  203. if bbpath:
  204. bitbakeconf = bb.utils.which(bbpath, "conf/bitbake.conf")
  205. if bitbakeconf:
  206. return os.path.dirname(os.path.dirname(bitbakeconf))
  207. return None
  208. class CookerDataBuilder(object):
  209. def __init__(self, cookercfg, worker = False):
  210. self.prefiles = cookercfg.prefile
  211. self.postfiles = cookercfg.postfile
  212. self.tracking = cookercfg.tracking
  213. bb.utils.set_context(bb.utils.clean_context())
  214. bb.event.set_class_handlers(bb.event.clean_class_handlers())
  215. self.basedata = bb.data.init()
  216. if self.tracking:
  217. self.basedata.enableTracking()
  218. # Keep a datastore of the initial environment variables and their
  219. # values from when BitBake was launched to enable child processes
  220. # to use environment variables which have been cleaned from the
  221. # BitBake processes env
  222. self.savedenv = bb.data.init()
  223. for k in cookercfg.env:
  224. self.savedenv.setVar(k, cookercfg.env[k])
  225. filtered_keys = bb.utils.approved_variables()
  226. bb.data.inheritFromOS(self.basedata, self.savedenv, filtered_keys)
  227. self.basedata.setVar("BB_ORIGENV", self.savedenv)
  228. if worker:
  229. self.basedata.setVar("BB_WORKERCONTEXT", "1")
  230. self.data = self.basedata
  231. self.mcdata = {}
  232. def parseBaseConfiguration(self):
  233. try:
  234. bb.parse.init_parser(self.basedata)
  235. self.data = self.parseConfigurationFiles(self.prefiles, self.postfiles)
  236. if self.data.getVar("BB_WORKERCONTEXT", False) is None:
  237. bb.fetch.fetcher_init(self.data)
  238. bb.codeparser.parser_cache_init(self.data)
  239. bb.event.fire(bb.event.ConfigParsed(), self.data)
  240. reparse_cnt = 0
  241. while self.data.getVar("BB_INVALIDCONF", False) is True:
  242. if reparse_cnt > 20:
  243. logger.error("Configuration has been re-parsed over 20 times, "
  244. "breaking out of the loop...")
  245. raise Exception("Too deep config re-parse loop. Check locations where "
  246. "BB_INVALIDCONF is being set (ConfigParsed event handlers)")
  247. self.data.setVar("BB_INVALIDCONF", False)
  248. self.data = self.parseConfigurationFiles(self.prefiles, self.postfiles)
  249. reparse_cnt += 1
  250. bb.event.fire(bb.event.ConfigParsed(), self.data)
  251. bb.parse.init_parser(self.data)
  252. self.data_hash = self.data.get_hash()
  253. self.mcdata[''] = self.data
  254. multiconfig = (self.data.getVar("BBMULTICONFIG") or "").split()
  255. for config in multiconfig:
  256. mcdata = self.parseConfigurationFiles(self.prefiles, self.postfiles, config)
  257. bb.event.fire(bb.event.ConfigParsed(), mcdata)
  258. self.mcdata[config] = mcdata
  259. if multiconfig:
  260. bb.event.fire(bb.event.MultiConfigParsed(self.mcdata), self.data)
  261. except (SyntaxError, bb.BBHandledException):
  262. raise bb.BBHandledException
  263. except bb.data_smart.ExpansionError as e:
  264. logger.error(str(e))
  265. raise bb.BBHandledException
  266. except Exception:
  267. logger.exception("Error parsing configuration files")
  268. raise bb.BBHandledException
  269. # Create a copy so we can reset at a later date when UIs disconnect
  270. self.origdata = self.data
  271. self.data = bb.data.createCopy(self.origdata)
  272. self.mcdata[''] = self.data
  273. def reset(self):
  274. # We may not have run parseBaseConfiguration() yet
  275. if not hasattr(self, 'origdata'):
  276. return
  277. self.data = bb.data.createCopy(self.origdata)
  278. self.mcdata[''] = self.data
  279. def _findLayerConf(self, data):
  280. return findConfigFile("bblayers.conf", data)
  281. def parseConfigurationFiles(self, prefiles, postfiles, mc = "default"):
  282. data = bb.data.createCopy(self.basedata)
  283. data.setVar("BB_CURRENT_MC", mc)
  284. # Parse files for loading *before* bitbake.conf and any includes
  285. for f in prefiles:
  286. data = parse_config_file(f, data)
  287. layerconf = self._findLayerConf(data)
  288. if layerconf:
  289. parselog.debug(2, "Found bblayers.conf (%s)", layerconf)
  290. # By definition bblayers.conf is in conf/ of TOPDIR.
  291. # We may have been called with cwd somewhere else so reset TOPDIR
  292. data.setVar("TOPDIR", os.path.dirname(os.path.dirname(layerconf)))
  293. data = parse_config_file(layerconf, data)
  294. layers = (data.getVar('BBLAYERS') or "").split()
  295. data = bb.data.createCopy(data)
  296. approved = bb.utils.approved_variables()
  297. for layer in layers:
  298. if not os.path.isdir(layer):
  299. parselog.critical("Layer directory '%s' does not exist! "
  300. "Please check BBLAYERS in %s" % (layer, layerconf))
  301. sys.exit(1)
  302. parselog.debug(2, "Adding layer %s", layer)
  303. if 'HOME' in approved and '~' in layer:
  304. layer = os.path.expanduser(layer)
  305. if layer.endswith('/'):
  306. layer = layer.rstrip('/')
  307. data.setVar('LAYERDIR', layer)
  308. data.setVar('LAYERDIR_RE', re.escape(layer))
  309. data = parse_config_file(os.path.join(layer, "conf", "layer.conf"), data)
  310. data.expandVarref('LAYERDIR')
  311. data.expandVarref('LAYERDIR_RE')
  312. data.delVar('LAYERDIR_RE')
  313. data.delVar('LAYERDIR')
  314. bbfiles_dynamic = (data.getVar('BBFILES_DYNAMIC') or "").split()
  315. collections = (data.getVar('BBFILE_COLLECTIONS') or "").split()
  316. invalid = []
  317. for entry in bbfiles_dynamic:
  318. parts = entry.split(":", 1)
  319. if len(parts) != 2:
  320. invalid.append(entry)
  321. continue
  322. l, f = parts
  323. if l in collections:
  324. data.appendVar("BBFILES", " " + f)
  325. if invalid:
  326. bb.fatal("BBFILES_DYNAMIC entries must be of the form <collection name>:<filename pattern>, not:\n %s" % "\n ".join(invalid))
  327. layerseries = set((data.getVar("LAYERSERIES_CORENAMES") or "").split())
  328. collections_tmp = collections[:]
  329. for c in collections:
  330. collections_tmp.remove(c)
  331. if c in collections_tmp:
  332. bb.fatal("Found duplicated BBFILE_COLLECTIONS '%s', check bblayers.conf or layer.conf to fix it." % c)
  333. compat = set((data.getVar("LAYERSERIES_COMPAT_%s" % c) or "").split())
  334. if compat and not (compat & layerseries):
  335. bb.fatal("Layer %s is not compatible with the core layer which only supports these series: %s (layer is compatible with %s)"
  336. % (c, " ".join(layerseries), " ".join(compat)))
  337. elif not compat and not data.getVar("BB_WORKERCONTEXT"):
  338. bb.warn("Layer %s should set LAYERSERIES_COMPAT_%s in its conf/layer.conf file to list the core layer names it is compatible with." % (c, c))
  339. if not data.getVar("BBPATH"):
  340. msg = "The BBPATH variable is not set"
  341. if not layerconf:
  342. msg += (" and bitbake did not find a conf/bblayers.conf file in"
  343. " the expected location.\nMaybe you accidentally"
  344. " invoked bitbake from the wrong directory?")
  345. raise SystemExit(msg)
  346. data = parse_config_file(os.path.join("conf", "bitbake.conf"), data)
  347. # Parse files for loading *after* bitbake.conf and any includes
  348. for p in postfiles:
  349. data = parse_config_file(p, data)
  350. # Handle any INHERITs and inherit the base class
  351. bbclasses = ["base"] + (data.getVar('INHERIT') or "").split()
  352. for bbclass in bbclasses:
  353. data = _inherit(bbclass, data)
  354. # Nomally we only register event handlers at the end of parsing .bb files
  355. # We register any handlers we've found so far here...
  356. for var in data.getVar('__BBHANDLERS', False) or []:
  357. handlerfn = data.getVarFlag(var, "filename", False)
  358. if not handlerfn:
  359. parselog.critical("Undefined event handler function '%s'" % var)
  360. sys.exit(1)
  361. handlerln = int(data.getVarFlag(var, "lineno", False))
  362. bb.event.register(var, data.getVar(var, False), (data.getVarFlag(var, "eventmask") or "").split(), handlerfn, handlerln)
  363. data.setVar('BBINCLUDED',bb.parse.get_file_depends(data))
  364. return data