1 2# 3# Copyright (C) 2003, 2004 Chris Larson 4# Copyright (C) 2003, 2004 Phil Blundell 5# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer 6# Copyright (C) 2005 Holger Hans Peter Freyther 7# Copyright (C) 2005 ROAD GmbH 8# Copyright (C) 2006 Richard Purdie 9# 10# SPDX-License-Identifier: GPL-2.0-only 11# 12 13import logging 14import os 15import re 16import sys 17import hashlib 18from functools import wraps 19import bb 20from bb import data 21import bb.parse 22 23logger = logging.getLogger("BitBake") 24parselog = logging.getLogger("BitBake.Parsing") 25 26class ConfigParameters(object): 27 def __init__(self, argv=None): 28 self.options, targets = self.parseCommandLine(argv or sys.argv) 29 self.environment = self.parseEnvironment() 30 31 self.options.pkgs_to_build = targets or [] 32 33 for key, val in self.options.__dict__.items(): 34 setattr(self, key, val) 35 36 def parseCommandLine(self, argv=sys.argv): 37 raise Exception("Caller must implement commandline option parsing") 38 39 def parseEnvironment(self): 40 return os.environ.copy() 41 42 def updateFromServer(self, server): 43 if not self.options.cmd: 44 defaulttask, error = server.runCommand(["getVariable", "BB_DEFAULT_TASK"]) 45 if error: 46 raise Exception("Unable to get the value of BB_DEFAULT_TASK from the server: %s" % error) 47 self.options.cmd = defaulttask or "build" 48 _, error = server.runCommand(["setConfig", "cmd", self.options.cmd]) 49 if error: 50 raise Exception("Unable to set configuration option 'cmd' on the server: %s" % error) 51 52 if not self.options.pkgs_to_build: 53 bbpkgs, error = server.runCommand(["getVariable", "BBTARGETS"]) 54 if error: 55 raise Exception("Unable to get the value of BBTARGETS from the server: %s" % error) 56 if bbpkgs: 57 self.options.pkgs_to_build.extend(bbpkgs.split()) 58 59 def updateToServer(self, server, environment): 60 options = {} 61 for o in ["halt", "force", "invalidate_stamp", 62 "dry_run", "dump_signatures", 63 "extra_assume_provided", "profile", 64 "prefile", "postfile", "server_timeout", 65 "nosetscene", "setsceneonly", "skipsetscene", 66 "runall", "runonly", "writeeventlog"]: 67 options[o] = getattr(self.options, o) 68 69 options['build_verbose_shell'] = self.options.verbose 70 options['build_verbose_stdout'] = self.options.verbose 71 options['default_loglevel'] = bb.msg.loggerDefaultLogLevel 72 options['debug_domains'] = bb.msg.loggerDefaultDomains 73 74 ret, error = server.runCommand(["updateConfig", options, environment, sys.argv]) 75 if error: 76 raise Exception("Unable to update the server configuration with local parameters: %s" % error) 77 78 def parseActions(self): 79 # Parse any commandline into actions 80 action = {'action':None, 'msg':None} 81 if self.options.show_environment: 82 if 'world' in self.options.pkgs_to_build: 83 action['msg'] = "'world' is not a valid target for --environment." 84 elif 'universe' in self.options.pkgs_to_build: 85 action['msg'] = "'universe' is not a valid target for --environment." 86 elif len(self.options.pkgs_to_build) > 1: 87 action['msg'] = "Only one target can be used with the --environment option." 88 elif self.options.buildfile and len(self.options.pkgs_to_build) > 0: 89 action['msg'] = "No target should be used with the --environment and --buildfile options." 90 elif self.options.pkgs_to_build: 91 action['action'] = ["showEnvironmentTarget", self.options.pkgs_to_build] 92 else: 93 action['action'] = ["showEnvironment", self.options.buildfile] 94 elif self.options.buildfile is not None: 95 action['action'] = ["buildFile", self.options.buildfile, self.options.cmd] 96 elif self.options.revisions_changed: 97 action['action'] = ["compareRevisions"] 98 elif self.options.show_versions: 99 action['action'] = ["showVersions"] 100 elif self.options.parse_only: 101 action['action'] = ["parseFiles"] 102 elif self.options.dot_graph: 103 if self.options.pkgs_to_build: 104 action['action'] = ["generateDotGraph", self.options.pkgs_to_build, self.options.cmd] 105 else: 106 action['msg'] = "Please specify a package name for dependency graph generation." 107 else: 108 if self.options.pkgs_to_build: 109 action['action'] = ["buildTargets", self.options.pkgs_to_build, self.options.cmd] 110 else: 111 #action['msg'] = "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information." 112 action = None 113 self.options.initialaction = action 114 return action 115 116class CookerConfiguration(object): 117 """ 118 Manages build options and configurations for one run 119 """ 120 121 def __init__(self): 122 self.debug_domains = bb.msg.loggerDefaultDomains 123 self.default_loglevel = bb.msg.loggerDefaultLogLevel 124 self.extra_assume_provided = [] 125 self.prefile = [] 126 self.postfile = [] 127 self.cmd = None 128 self.halt = True 129 self.force = False 130 self.profile = False 131 self.nosetscene = False 132 self.setsceneonly = False 133 self.skipsetscene = False 134 self.invalidate_stamp = False 135 self.dump_signatures = [] 136 self.build_verbose_shell = False 137 self.build_verbose_stdout = False 138 self.dry_run = False 139 self.tracking = False 140 self.writeeventlog = False 141 self.limited_deps = False 142 self.runall = [] 143 self.runonly = [] 144 145 self.env = {} 146 147 def __getstate__(self): 148 state = {} 149 for key in self.__dict__.keys(): 150 state[key] = getattr(self, key) 151 return state 152 153 def __setstate__(self,state): 154 for k in state: 155 setattr(self, k, state[k]) 156 157 158def catch_parse_error(func): 159 """Exception handling bits for our parsing""" 160 @wraps(func) 161 def wrapped(fn, *args): 162 try: 163 return func(fn, *args) 164 except Exception as exc: 165 import traceback 166 167 bbdir = os.path.dirname(__file__) + os.sep 168 exc_class, exc, tb = sys.exc_info() 169 for tb in iter(lambda: tb.tb_next, None): 170 # Skip frames in bitbake itself, we only want the metadata 171 fn, _, _, _ = traceback.extract_tb(tb, 1)[0] 172 if not fn.startswith(bbdir): 173 break 174 parselog.critical("Unable to parse %s" % fn, exc_info=(exc_class, exc, tb)) 175 raise bb.BBHandledException() 176 return wrapped 177 178@catch_parse_error 179def parse_config_file(fn, data, include=True): 180 return bb.parse.handle(fn, data, include, baseconfig=True) 181 182@catch_parse_error 183def _inherit(bbclass, data): 184 bb.parse.BBHandler.inherit(bbclass, "configuration INHERITs", 0, data) 185 return data 186 187def findConfigFile(configfile, data): 188 search = [] 189 bbpath = data.getVar("BBPATH") 190 if bbpath: 191 for i in bbpath.split(":"): 192 search.append(os.path.join(i, "conf", configfile)) 193 path = os.getcwd() 194 while path != "/": 195 search.append(os.path.join(path, "conf", configfile)) 196 path, _ = os.path.split(path) 197 198 for i in search: 199 if os.path.exists(i): 200 return i 201 202 return None 203 204# 205# We search for a conf/bblayers.conf under an entry in BBPATH or in cwd working 206# up to /. If that fails, bitbake would fall back to cwd. 207# 208 209def findTopdir(): 210 d = bb.data.init() 211 bbpath = None 212 if 'BBPATH' in os.environ: 213 bbpath = os.environ['BBPATH'] 214 d.setVar('BBPATH', bbpath) 215 216 layerconf = findConfigFile("bblayers.conf", d) 217 if layerconf: 218 return os.path.dirname(os.path.dirname(layerconf)) 219 220 return os.path.abspath(os.getcwd()) 221 222class CookerDataBuilder(object): 223 224 def __init__(self, cookercfg, worker = False): 225 226 self.prefiles = cookercfg.prefile 227 self.postfiles = cookercfg.postfile 228 self.tracking = cookercfg.tracking 229 230 bb.utils.set_context(bb.utils.clean_context()) 231 bb.event.set_class_handlers(bb.event.clean_class_handlers()) 232 self.basedata = bb.data.init() 233 if self.tracking: 234 self.basedata.enableTracking() 235 236 # Keep a datastore of the initial environment variables and their 237 # values from when BitBake was launched to enable child processes 238 # to use environment variables which have been cleaned from the 239 # BitBake processes env 240 self.savedenv = bb.data.init() 241 for k in cookercfg.env: 242 self.savedenv.setVar(k, cookercfg.env[k]) 243 if k in bb.data_smart.bitbake_renamed_vars: 244 bb.error('Shell environment variable %s has been renamed to %s' % (k, bb.data_smart.bitbake_renamed_vars[k])) 245 bb.fatal("Exiting to allow enviroment variables to be corrected") 246 247 filtered_keys = bb.utils.approved_variables() 248 bb.data.inheritFromOS(self.basedata, self.savedenv, filtered_keys) 249 self.basedata.setVar("BB_ORIGENV", self.savedenv) 250 self.basedata.setVar("__bbclasstype", "global") 251 252 if worker: 253 self.basedata.setVar("BB_WORKERCONTEXT", "1") 254 255 self.data = self.basedata 256 self.mcdata = {} 257 258 def calc_datastore_hashes(self): 259 data_hash = hashlib.sha256() 260 data_hash.update(self.data.get_hash().encode('utf-8')) 261 multiconfig = (self.data.getVar("BBMULTICONFIG") or "").split() 262 for config in multiconfig: 263 data_hash.update(self.mcdata[config].get_hash().encode('utf-8')) 264 self.data_hash = data_hash.hexdigest() 265 266 def parseBaseConfiguration(self, worker=False): 267 mcdata = {} 268 try: 269 self.data = self.parseConfigurationFiles(self.prefiles, self.postfiles) 270 271 servercontext = self.data.getVar("BB_WORKERCONTEXT", False) is None and not worker 272 bb.fetch.fetcher_init(self.data, servercontext) 273 bb.parse.init_parser(self.data) 274 275 bb.event.fire(bb.event.ConfigParsed(), self.data) 276 277 reparse_cnt = 0 278 while self.data.getVar("BB_INVALIDCONF", False) is True: 279 if reparse_cnt > 20: 280 logger.error("Configuration has been re-parsed over 20 times, " 281 "breaking out of the loop...") 282 raise Exception("Too deep config re-parse loop. Check locations where " 283 "BB_INVALIDCONF is being set (ConfigParsed event handlers)") 284 self.data.setVar("BB_INVALIDCONF", False) 285 self.data = self.parseConfigurationFiles(self.prefiles, self.postfiles) 286 reparse_cnt += 1 287 bb.event.fire(bb.event.ConfigParsed(), self.data) 288 289 bb.parse.init_parser(self.data) 290 mcdata[''] = self.data 291 292 multiconfig = (self.data.getVar("BBMULTICONFIG") or "").split() 293 for config in multiconfig: 294 if config[0].isdigit(): 295 bb.fatal("Multiconfig name '%s' is invalid as multiconfigs cannot start with a digit" % config) 296 parsed_mcdata = self.parseConfigurationFiles(self.prefiles, self.postfiles, config) 297 bb.event.fire(bb.event.ConfigParsed(), parsed_mcdata) 298 mcdata[config] = parsed_mcdata 299 if multiconfig: 300 bb.event.fire(bb.event.MultiConfigParsed(mcdata), self.data) 301 302 except bb.data_smart.ExpansionError as e: 303 logger.error(str(e)) 304 raise bb.BBHandledException() 305 306 bb.codeparser.update_module_dependencies(self.data) 307 308 # Handle obsolete variable names 309 d = self.data 310 renamedvars = d.getVarFlags('BB_RENAMED_VARIABLES') or {} 311 renamedvars.update(bb.data_smart.bitbake_renamed_vars) 312 issues = False 313 for v in renamedvars: 314 if d.getVar(v) != None or d.hasOverrides(v): 315 issues = True 316 loginfo = {} 317 history = d.varhistory.get_variable_refs(v) 318 for h in history: 319 for line in history[h]: 320 loginfo = {'file' : h, 'line' : line} 321 bb.data.data_smart._print_rename_error(v, loginfo, renamedvars) 322 if not history: 323 bb.data.data_smart._print_rename_error(v, loginfo, renamedvars) 324 if issues: 325 raise bb.BBHandledException() 326 327 for mc in mcdata: 328 mcdata[mc].renameVar("__depends", "__base_depends") 329 mcdata[mc].setVar("__bbclasstype", "recipe") 330 331 # Create a copy so we can reset at a later date when UIs disconnect 332 self.mcorigdata = mcdata 333 for mc in mcdata: 334 self.mcdata[mc] = bb.data.createCopy(mcdata[mc]) 335 self.data = self.mcdata[''] 336 self.calc_datastore_hashes() 337 338 def reset(self): 339 # We may not have run parseBaseConfiguration() yet 340 if not hasattr(self, 'mcorigdata'): 341 return 342 for mc in self.mcorigdata: 343 self.mcdata[mc] = bb.data.createCopy(self.mcorigdata[mc]) 344 self.data = self.mcdata[''] 345 346 def _findLayerConf(self, data): 347 return findConfigFile("bblayers.conf", data) 348 349 def parseConfigurationFiles(self, prefiles, postfiles, mc = "default"): 350 data = bb.data.createCopy(self.basedata) 351 data.setVar("BB_CURRENT_MC", mc) 352 353 # Parse files for loading *before* bitbake.conf and any includes 354 for f in prefiles: 355 data = parse_config_file(f, data) 356 357 layerconf = self._findLayerConf(data) 358 if layerconf: 359 parselog.debug2("Found bblayers.conf (%s)", layerconf) 360 # By definition bblayers.conf is in conf/ of TOPDIR. 361 # We may have been called with cwd somewhere else so reset TOPDIR 362 data.setVar("TOPDIR", os.path.dirname(os.path.dirname(layerconf))) 363 data = parse_config_file(layerconf, data) 364 365 if not data.getVar("BB_CACHEDIR"): 366 data.setVar("BB_CACHEDIR", "${TOPDIR}/cache") 367 368 bb.codeparser.parser_cache_init(data.getVar("BB_CACHEDIR")) 369 370 layers = (data.getVar('BBLAYERS') or "").split() 371 broken_layers = [] 372 373 if not layers: 374 bb.fatal("The bblayers.conf file doesn't contain any BBLAYERS definition") 375 376 data = bb.data.createCopy(data) 377 approved = bb.utils.approved_variables() 378 379 # Check whether present layer directories exist 380 for layer in layers: 381 if not os.path.isdir(layer): 382 broken_layers.append(layer) 383 384 if broken_layers: 385 parselog.critical("The following layer directories do not exist:") 386 for layer in broken_layers: 387 parselog.critical(" %s", layer) 388 parselog.critical("Please check BBLAYERS in %s" % (layerconf)) 389 raise bb.BBHandledException() 390 391 layerseries = None 392 compat_entries = {} 393 for layer in layers: 394 parselog.debug2("Adding layer %s", layer) 395 if 'HOME' in approved and '~' in layer: 396 layer = os.path.expanduser(layer) 397 if layer.endswith('/'): 398 layer = layer.rstrip('/') 399 data.setVar('LAYERDIR', layer) 400 data.setVar('LAYERDIR_RE', re.escape(layer)) 401 data = parse_config_file(os.path.join(layer, "conf", "layer.conf"), data) 402 data.expandVarref('LAYERDIR') 403 data.expandVarref('LAYERDIR_RE') 404 405 # Sadly we can't have nice things. 406 # Some layers think they're going to be 'clever' and copy the values from 407 # another layer, e.g. using ${LAYERSERIES_COMPAT_core}. The whole point of 408 # this mechanism is to make it clear which releases a layer supports and 409 # show when a layer master branch is bitrotting and is unmaintained. 410 # We therefore avoid people doing this here. 411 collections = (data.getVar('BBFILE_COLLECTIONS') or "").split() 412 for c in collections: 413 compat_entry = data.getVar("LAYERSERIES_COMPAT_%s" % c) 414 if compat_entry: 415 compat_entries[c] = set(compat_entry.split()) 416 data.delVar("LAYERSERIES_COMPAT_%s" % c) 417 if not layerseries: 418 layerseries = set((data.getVar("LAYERSERIES_CORENAMES") or "").split()) 419 if layerseries: 420 data.delVar("LAYERSERIES_CORENAMES") 421 422 data.delVar('LAYERDIR_RE') 423 data.delVar('LAYERDIR') 424 for c in compat_entries: 425 data.setVar("LAYERSERIES_COMPAT_%s" % c, " ".join(sorted(compat_entries[c]))) 426 427 bbfiles_dynamic = (data.getVar('BBFILES_DYNAMIC') or "").split() 428 collections = (data.getVar('BBFILE_COLLECTIONS') or "").split() 429 invalid = [] 430 for entry in bbfiles_dynamic: 431 parts = entry.split(":", 1) 432 if len(parts) != 2: 433 invalid.append(entry) 434 continue 435 l, f = parts 436 invert = l[0] == "!" 437 if invert: 438 l = l[1:] 439 if (l in collections and not invert) or (l not in collections and invert): 440 data.appendVar("BBFILES", " " + f) 441 if invalid: 442 bb.fatal("BBFILES_DYNAMIC entries must be of the form {!}<collection name>:<filename pattern>, not:\n %s" % "\n ".join(invalid)) 443 444 collections_tmp = collections[:] 445 for c in collections: 446 collections_tmp.remove(c) 447 if c in collections_tmp: 448 bb.fatal("Found duplicated BBFILE_COLLECTIONS '%s', check bblayers.conf or layer.conf to fix it." % c) 449 450 compat = set() 451 if c in compat_entries: 452 compat = compat_entries[c] 453 if compat and not layerseries: 454 bb.fatal("No core layer found to work with layer '%s'. Missing entry in bblayers.conf?" % c) 455 if compat and not (compat & layerseries): 456 bb.fatal("Layer %s is not compatible with the core layer which only supports these series: %s (layer is compatible with %s)" 457 % (c, " ".join(layerseries), " ".join(compat))) 458 elif not compat and not data.getVar("BB_WORKERCONTEXT"): 459 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)) 460 461 data.setVar("LAYERSERIES_CORENAMES", " ".join(sorted(layerseries))) 462 463 if not data.getVar("BBPATH"): 464 msg = "The BBPATH variable is not set" 465 if not layerconf: 466 msg += (" and bitbake did not find a conf/bblayers.conf file in" 467 " the expected location.\nMaybe you accidentally" 468 " invoked bitbake from the wrong directory?") 469 bb.fatal(msg) 470 471 if not data.getVar("TOPDIR"): 472 data.setVar("TOPDIR", os.path.abspath(os.getcwd())) 473 if not data.getVar("BB_CACHEDIR"): 474 data.setVar("BB_CACHEDIR", "${TOPDIR}/cache") 475 bb.codeparser.parser_cache_init(data.getVar("BB_CACHEDIR")) 476 477 data = parse_config_file(os.path.join("conf", "bitbake.conf"), data) 478 479 # Parse files for loading *after* bitbake.conf and any includes 480 for p in postfiles: 481 data = parse_config_file(p, data) 482 483 # Handle any INHERITs and inherit the base class 484 bbclasses = ["base"] + (data.getVar('INHERIT') or "").split() 485 for bbclass in bbclasses: 486 data = _inherit(bbclass, data) 487 488 # Normally we only register event handlers at the end of parsing .bb files 489 # We register any handlers we've found so far here... 490 for var in data.getVar('__BBHANDLERS', False) or []: 491 handlerfn = data.getVarFlag(var, "filename", False) 492 if not handlerfn: 493 parselog.critical("Undefined event handler function '%s'" % var) 494 raise bb.BBHandledException() 495 handlerln = int(data.getVarFlag(var, "lineno", False)) 496 bb.event.register(var, data.getVar(var, False), (data.getVarFlag(var, "eventmask") or "").split(), handlerfn, handlerln, data) 497 498 data.setVar('BBINCLUDED',bb.parse.get_file_depends(data)) 499 500 return data 501 502 @staticmethod 503 def _parse_recipe(bb_data, bbfile, appends, mc, layername): 504 bb_data.setVar("__BBMULTICONFIG", mc) 505 bb_data.setVar("FILE_LAYERNAME", layername) 506 507 bbfile_loc = os.path.abspath(os.path.dirname(bbfile)) 508 bb.parse.cached_mtime_noerror(bbfile_loc) 509 510 if appends: 511 bb_data.setVar('__BBAPPEND', " ".join(appends)) 512 513 return bb.parse.handle(bbfile, bb_data) 514 515 def parseRecipeVariants(self, bbfile, appends, virtonly=False, mc=None, layername=None): 516 """ 517 Load and parse one .bb build file 518 Return the data and whether parsing resulted in the file being skipped 519 """ 520 521 if virtonly: 522 (bbfile, virtual, mc) = bb.cache.virtualfn2realfn(bbfile) 523 bb_data = self.mcdata[mc].createCopy() 524 bb_data.setVar("__ONLYFINALISE", virtual or "default") 525 return self._parse_recipe(bb_data, bbfile, appends, mc, layername) 526 527 if mc is not None: 528 bb_data = self.mcdata[mc].createCopy() 529 return self._parse_recipe(bb_data, bbfile, appends, mc, layername) 530 531 bb_data = self.data.createCopy() 532 datastores = self._parse_recipe(bb_data, bbfile, appends, '', layername) 533 534 for mc in self.mcdata: 535 if not mc: 536 continue 537 bb_data = self.mcdata[mc].createCopy() 538 newstores = self._parse_recipe(bb_data, bbfile, appends, mc, layername) 539 for ns in newstores: 540 datastores["mc:%s:%s" % (mc, ns)] = newstores[ns] 541 542 return datastores 543 544 def parseRecipe(self, virtualfn, appends, layername): 545 """ 546 Return a complete set of data for fn. 547 To do this, we need to parse the file. 548 """ 549 logger.debug("Parsing %s (full)" % virtualfn) 550 (fn, virtual, mc) = bb.cache.virtualfn2realfn(virtualfn) 551 datastores = self.parseRecipeVariants(virtualfn, appends, virtonly=True, layername=layername) 552 return datastores[virtual] 553