blob: b07c266439358eda16ad8d54753c826dab1206b2 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#!/usr/bin/env python
2# ex:ts=4:sw=4:sts=4:et
3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4#
5# Copyright (C) 2003, 2004 Chris Larson
6# Copyright (C) 2003, 2004 Phil Blundell
7# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
8# Copyright (C) 2005 Holger Hans Peter Freyther
9# Copyright (C) 2005 ROAD GmbH
10# Copyright (C) 2006 Richard Purdie
11#
12# This program is free software; you can redistribute it and/or modify
13# it under the terms of the GNU General Public License version 2 as
14# published by the Free Software Foundation.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License along
22# with this program; if not, write to the Free Software Foundation, Inc.,
23# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24
Patrick Williamsc124f4f2015-09-15 14:41:29 -050025import logging
Patrick Williamsc0f7c042017-02-23 20:41:17 -060026import os
27import re
28import sys
29from functools import wraps
Patrick Williamsc124f4f2015-09-15 14:41:29 -050030import bb
31from bb import data
32import bb.parse
33
34logger = logging.getLogger("BitBake")
35parselog = logging.getLogger("BitBake.Parsing")
36
37class ConfigParameters(object):
38 def __init__(self, argv=sys.argv):
39 self.options, targets = self.parseCommandLine(argv)
40 self.environment = self.parseEnvironment()
41
42 self.options.pkgs_to_build = targets or []
43
44 self.options.tracking = False
45 if hasattr(self.options, "show_environment") and self.options.show_environment:
46 self.options.tracking = True
47
48 for key, val in self.options.__dict__.items():
49 setattr(self, key, val)
50
51 def parseCommandLine(self, argv=sys.argv):
52 raise Exception("Caller must implement commandline option parsing")
53
54 def parseEnvironment(self):
55 return os.environ.copy()
56
57 def updateFromServer(self, server):
58 if not self.options.cmd:
59 defaulttask, error = server.runCommand(["getVariable", "BB_DEFAULT_TASK"])
60 if error:
61 raise Exception("Unable to get the value of BB_DEFAULT_TASK from the server: %s" % error)
62 self.options.cmd = defaulttask or "build"
63 _, error = server.runCommand(["setConfig", "cmd", self.options.cmd])
64 if error:
65 raise Exception("Unable to set configuration option 'cmd' on the server: %s" % error)
66
67 if not self.options.pkgs_to_build:
Patrick Williamsf1e5d692016-03-30 15:21:19 -050068 bbpkgs, error = server.runCommand(["getVariable", "BBTARGETS"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050069 if error:
Patrick Williamsf1e5d692016-03-30 15:21:19 -050070 raise Exception("Unable to get the value of BBTARGETS from the server: %s" % error)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050071 if bbpkgs:
72 self.options.pkgs_to_build.extend(bbpkgs.split())
73
74 def updateToServer(self, server, environment):
75 options = {}
76 for o in ["abort", "tryaltconfigs", "force", "invalidate_stamp",
77 "verbose", "debug", "dry_run", "dump_signatures",
78 "debug_domains", "extra_assume_provided", "profile",
79 "prefile", "postfile"]:
80 options[o] = getattr(self.options, o)
81
82 ret, error = server.runCommand(["updateConfig", options, environment])
83 if error:
84 raise Exception("Unable to update the server configuration with local parameters: %s" % error)
85
86 def parseActions(self):
87 # Parse any commandline into actions
88 action = {'action':None, 'msg':None}
89 if self.options.show_environment:
90 if 'world' in self.options.pkgs_to_build:
91 action['msg'] = "'world' is not a valid target for --environment."
92 elif 'universe' in self.options.pkgs_to_build:
93 action['msg'] = "'universe' is not a valid target for --environment."
94 elif len(self.options.pkgs_to_build) > 1:
95 action['msg'] = "Only one target can be used with the --environment option."
96 elif self.options.buildfile and len(self.options.pkgs_to_build) > 0:
97 action['msg'] = "No target should be used with the --environment and --buildfile options."
98 elif len(self.options.pkgs_to_build) > 0:
99 action['action'] = ["showEnvironmentTarget", self.options.pkgs_to_build]
100 else:
101 action['action'] = ["showEnvironment", self.options.buildfile]
102 elif self.options.buildfile is not None:
103 action['action'] = ["buildFile", self.options.buildfile, self.options.cmd]
104 elif self.options.revisions_changed:
105 action['action'] = ["compareRevisions"]
106 elif self.options.show_versions:
107 action['action'] = ["showVersions"]
108 elif self.options.parse_only:
109 action['action'] = ["parseFiles"]
110 elif self.options.dot_graph:
111 if self.options.pkgs_to_build:
112 action['action'] = ["generateDotGraph", self.options.pkgs_to_build, self.options.cmd]
113 else:
114 action['msg'] = "Please specify a package name for dependency graph generation."
115 else:
116 if self.options.pkgs_to_build:
117 action['action'] = ["buildTargets", self.options.pkgs_to_build, self.options.cmd]
118 else:
119 #action['msg'] = "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information."
120 action = None
121 self.options.initialaction = action
122 return action
123
124class CookerConfiguration(object):
125 """
126 Manages build options and configurations for one run
127 """
128
129 def __init__(self):
130 self.debug_domains = []
131 self.extra_assume_provided = []
132 self.prefile = []
133 self.postfile = []
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500134 self.prefile_server = []
135 self.postfile_server = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500136 self.debug = 0
137 self.cmd = None
138 self.abort = True
139 self.force = False
140 self.profile = False
141 self.nosetscene = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500142 self.setsceneonly = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500143 self.invalidate_stamp = False
144 self.dump_signatures = []
145 self.dry_run = False
146 self.tracking = False
147 self.interface = []
148 self.writeeventlog = False
149
150 self.env = {}
151
152 def setConfigParameters(self, parameters):
153 for key in self.__dict__.keys():
154 if key in parameters.options.__dict__:
155 setattr(self, key, parameters.options.__dict__[key])
156 self.env = parameters.environment.copy()
157 self.tracking = parameters.tracking
158
159 def setServerRegIdleCallback(self, srcb):
160 self.server_register_idlecallback = srcb
161
162 def __getstate__(self):
163 state = {}
164 for key in self.__dict__.keys():
165 if key == "server_register_idlecallback":
166 state[key] = None
167 else:
168 state[key] = getattr(self, key)
169 return state
170
171 def __setstate__(self,state):
172 for k in state:
173 setattr(self, k, state[k])
174
175
176def catch_parse_error(func):
177 """Exception handling bits for our parsing"""
178 @wraps(func)
179 def wrapped(fn, *args):
180 try:
181 return func(fn, *args)
182 except IOError as exc:
183 import traceback
184 parselog.critical(traceback.format_exc())
185 parselog.critical("Unable to parse %s: %s" % (fn, exc))
186 sys.exit(1)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500187 except bb.data_smart.ExpansionError as exc:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500188 import traceback
189
190 bbdir = os.path.dirname(__file__) + os.sep
191 exc_class, exc, tb = sys.exc_info()
192 for tb in iter(lambda: tb.tb_next, None):
193 # Skip frames in bitbake itself, we only want the metadata
194 fn, _, _, _ = traceback.extract_tb(tb, 1)[0]
195 if not fn.startswith(bbdir):
196 break
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600197 parselog.critical("Unable to parse %s" % fn, exc_info=(exc_class, exc, tb))
198 sys.exit(1)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500199 except bb.parse.ParseError as exc:
200 parselog.critical(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500201 sys.exit(1)
202 return wrapped
203
204@catch_parse_error
205def parse_config_file(fn, data, include=True):
206 return bb.parse.handle(fn, data, include)
207
208@catch_parse_error
209def _inherit(bbclass, data):
210 bb.parse.BBHandler.inherit(bbclass, "configuration INHERITs", 0, data)
211 return data
212
213def findConfigFile(configfile, data):
214 search = []
215 bbpath = data.getVar("BBPATH", True)
216 if bbpath:
217 for i in bbpath.split(":"):
218 search.append(os.path.join(i, "conf", configfile))
219 path = os.getcwd()
220 while path != "/":
221 search.append(os.path.join(path, "conf", configfile))
222 path, _ = os.path.split(path)
223
224 for i in search:
225 if os.path.exists(i):
226 return i
227
228 return None
229
230class CookerDataBuilder(object):
231
232 def __init__(self, cookercfg, worker = False):
233
234 self.prefiles = cookercfg.prefile
235 self.postfiles = cookercfg.postfile
236 self.tracking = cookercfg.tracking
237
238 bb.utils.set_context(bb.utils.clean_context())
239 bb.event.set_class_handlers(bb.event.clean_class_handlers())
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600240 self.basedata = bb.data.init()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500241 if self.tracking:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600242 self.basedata.enableTracking()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500243
244 # Keep a datastore of the initial environment variables and their
245 # values from when BitBake was launched to enable child processes
246 # to use environment variables which have been cleaned from the
247 # BitBake processes env
248 self.savedenv = bb.data.init()
249 for k in cookercfg.env:
250 self.savedenv.setVar(k, cookercfg.env[k])
251
252 filtered_keys = bb.utils.approved_variables()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600253 bb.data.inheritFromOS(self.basedata, self.savedenv, filtered_keys)
254 self.basedata.setVar("BB_ORIGENV", self.savedenv)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500255
256 if worker:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600257 self.basedata.setVar("BB_WORKERCONTEXT", "1")
258
259 self.data = self.basedata
260 self.mcdata = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500261
262 def parseBaseConfiguration(self):
263 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600264 bb.parse.init_parser(self.basedata)
265 self.data = self.parseConfigurationFiles(self.prefiles, self.postfiles)
266
267 if self.data.getVar("BB_WORKERCONTEXT", False) is None:
268 bb.fetch.fetcher_init(self.data)
269 bb.codeparser.parser_cache_init(self.data)
270
271 bb.event.fire(bb.event.ConfigParsed(), self.data)
272
273 reparse_cnt = 0
274 while self.data.getVar("BB_INVALIDCONF", False) is True:
275 if reparse_cnt > 20:
276 logger.error("Configuration has been re-parsed over 20 times, "
277 "breaking out of the loop...")
278 raise Exception("Too deep config re-parse loop. Check locations where "
279 "BB_INVALIDCONF is being set (ConfigParsed event handlers)")
280 self.data.setVar("BB_INVALIDCONF", False)
281 self.data = self.parseConfigurationFiles(self.prefiles, self.postfiles)
282 reparse_cnt += 1
283 bb.event.fire(bb.event.ConfigParsed(), self.data)
284
285 bb.parse.init_parser(self.data)
286 self.data_hash = self.data.get_hash()
287 self.mcdata[''] = self.data
288
289 multiconfig = (self.data.getVar("BBMULTICONFIG", True) or "").split()
290 for config in multiconfig:
291 mcdata = self.parseConfigurationFiles(['conf/multiconfig/%s.conf' % config] + self.prefiles, self.postfiles)
292 bb.event.fire(bb.event.ConfigParsed(), mcdata)
293 self.mcdata[config] = mcdata
294
295 except (SyntaxError, bb.BBHandledException):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500296 raise bb.BBHandledException
297 except bb.data_smart.ExpansionError as e:
298 logger.error(str(e))
299 raise bb.BBHandledException
300 except Exception:
301 logger.exception("Error parsing configuration files")
302 raise bb.BBHandledException
303
304 def _findLayerConf(self, data):
305 return findConfigFile("bblayers.conf", data)
306
307 def parseConfigurationFiles(self, prefiles, postfiles):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600308 data = bb.data.createCopy(self.basedata)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500309
310 # Parse files for loading *before* bitbake.conf and any includes
311 for f in prefiles:
312 data = parse_config_file(f, data)
313
314 layerconf = self._findLayerConf(data)
315 if layerconf:
316 parselog.debug(2, "Found bblayers.conf (%s)", layerconf)
317 # By definition bblayers.conf is in conf/ of TOPDIR.
318 # We may have been called with cwd somewhere else so reset TOPDIR
319 data.setVar("TOPDIR", os.path.dirname(os.path.dirname(layerconf)))
320 data = parse_config_file(layerconf, data)
321
322 layers = (data.getVar('BBLAYERS', True) or "").split()
323
324 data = bb.data.createCopy(data)
325 approved = bb.utils.approved_variables()
326 for layer in layers:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600327 if not os.path.isdir(layer):
328 parselog.critical("Layer directory '%s' does not exist! "
329 "Please check BBLAYERS in %s" % (layer, layerconf))
330 sys.exit(1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500331 parselog.debug(2, "Adding layer %s", layer)
332 if 'HOME' in approved and '~' in layer:
333 layer = os.path.expanduser(layer)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500334 if layer.endswith('/'):
335 layer = layer.rstrip('/')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500336 data.setVar('LAYERDIR', layer)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600337 data.setVar('LAYERDIR_RE', re.escape(layer))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500338 data = parse_config_file(os.path.join(layer, "conf", "layer.conf"), data)
339 data.expandVarref('LAYERDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600340 data.expandVarref('LAYERDIR_RE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500341
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600342 data.delVar('LAYERDIR_RE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500343 data.delVar('LAYERDIR')
344
345 if not data.getVar("BBPATH", True):
346 msg = "The BBPATH variable is not set"
347 if not layerconf:
348 msg += (" and bitbake did not find a conf/bblayers.conf file in"
349 " the expected location.\nMaybe you accidentally"
350 " invoked bitbake from the wrong directory?")
351 raise SystemExit(msg)
352
353 data = parse_config_file(os.path.join("conf", "bitbake.conf"), data)
354
355 # Parse files for loading *after* bitbake.conf and any includes
356 for p in postfiles:
357 data = parse_config_file(p, data)
358
359 # Handle any INHERITs and inherit the base class
360 bbclasses = ["base"] + (data.getVar('INHERIT', True) or "").split()
361 for bbclass in bbclasses:
362 data = _inherit(bbclass, data)
363
364 # Nomally we only register event handlers at the end of parsing .bb files
365 # We register any handlers we've found so far here...
366 for var in data.getVar('__BBHANDLERS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500367 handlerfn = data.getVarFlag(var, "filename", False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600368 if not handlerfn:
369 parselog.critical("Undefined event handler function '%s'" % var)
370 sys.exit(1)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500371 handlerln = int(data.getVarFlag(var, "lineno", False))
372 bb.event.register(var, data.getVar(var, False), (data.getVarFlag(var, "eventmask", True) or "").split(), handlerfn, handlerln)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500373
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500374 data.setVar('BBINCLUDED',bb.parse.get_file_depends(data))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500375
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600376 return data
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500377