blob: 50259a9a073367c84a6df26228a201232e55d674 [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
25import os, sys
26from functools import wraps
27import logging
28import bb
29from bb import data
30import bb.parse
31
32logger = logging.getLogger("BitBake")
33parselog = logging.getLogger("BitBake.Parsing")
34
35class ConfigParameters(object):
36 def __init__(self, argv=sys.argv):
37 self.options, targets = self.parseCommandLine(argv)
38 self.environment = self.parseEnvironment()
39
40 self.options.pkgs_to_build = targets or []
41
42 self.options.tracking = False
43 if hasattr(self.options, "show_environment") and self.options.show_environment:
44 self.options.tracking = True
45
46 for key, val in self.options.__dict__.items():
47 setattr(self, key, val)
48
49 def parseCommandLine(self, argv=sys.argv):
50 raise Exception("Caller must implement commandline option parsing")
51
52 def parseEnvironment(self):
53 return os.environ.copy()
54
55 def updateFromServer(self, server):
56 if not self.options.cmd:
57 defaulttask, error = server.runCommand(["getVariable", "BB_DEFAULT_TASK"])
58 if error:
59 raise Exception("Unable to get the value of BB_DEFAULT_TASK from the server: %s" % error)
60 self.options.cmd = defaulttask or "build"
61 _, error = server.runCommand(["setConfig", "cmd", self.options.cmd])
62 if error:
63 raise Exception("Unable to set configuration option 'cmd' on the server: %s" % error)
64
65 if not self.options.pkgs_to_build:
Patrick Williamsf1e5d692016-03-30 15:21:19 -050066 bbpkgs, error = server.runCommand(["getVariable", "BBTARGETS"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050067 if error:
Patrick Williamsf1e5d692016-03-30 15:21:19 -050068 raise Exception("Unable to get the value of BBTARGETS from the server: %s" % error)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050069 if bbpkgs:
70 self.options.pkgs_to_build.extend(bbpkgs.split())
71
72 def updateToServer(self, server, environment):
73 options = {}
74 for o in ["abort", "tryaltconfigs", "force", "invalidate_stamp",
75 "verbose", "debug", "dry_run", "dump_signatures",
76 "debug_domains", "extra_assume_provided", "profile",
77 "prefile", "postfile"]:
78 options[o] = getattr(self.options, o)
79
80 ret, error = server.runCommand(["updateConfig", options, environment])
81 if error:
82 raise Exception("Unable to update the server configuration with local parameters: %s" % error)
83
84 def parseActions(self):
85 # Parse any commandline into actions
86 action = {'action':None, 'msg':None}
87 if self.options.show_environment:
88 if 'world' in self.options.pkgs_to_build:
89 action['msg'] = "'world' is not a valid target for --environment."
90 elif 'universe' in self.options.pkgs_to_build:
91 action['msg'] = "'universe' is not a valid target for --environment."
92 elif len(self.options.pkgs_to_build) > 1:
93 action['msg'] = "Only one target can be used with the --environment option."
94 elif self.options.buildfile and len(self.options.pkgs_to_build) > 0:
95 action['msg'] = "No target should be used with the --environment and --buildfile options."
96 elif len(self.options.pkgs_to_build) > 0:
97 action['action'] = ["showEnvironmentTarget", self.options.pkgs_to_build]
98 else:
99 action['action'] = ["showEnvironment", self.options.buildfile]
100 elif self.options.buildfile is not None:
101 action['action'] = ["buildFile", self.options.buildfile, self.options.cmd]
102 elif self.options.revisions_changed:
103 action['action'] = ["compareRevisions"]
104 elif self.options.show_versions:
105 action['action'] = ["showVersions"]
106 elif self.options.parse_only:
107 action['action'] = ["parseFiles"]
108 elif self.options.dot_graph:
109 if self.options.pkgs_to_build:
110 action['action'] = ["generateDotGraph", self.options.pkgs_to_build, self.options.cmd]
111 else:
112 action['msg'] = "Please specify a package name for dependency graph generation."
113 else:
114 if self.options.pkgs_to_build:
115 action['action'] = ["buildTargets", self.options.pkgs_to_build, self.options.cmd]
116 else:
117 #action['msg'] = "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information."
118 action = None
119 self.options.initialaction = action
120 return action
121
122class CookerConfiguration(object):
123 """
124 Manages build options and configurations for one run
125 """
126
127 def __init__(self):
128 self.debug_domains = []
129 self.extra_assume_provided = []
130 self.prefile = []
131 self.postfile = []
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500132 self.prefile_server = []
133 self.postfile_server = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500134 self.debug = 0
135 self.cmd = None
136 self.abort = True
137 self.force = False
138 self.profile = False
139 self.nosetscene = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500140 self.setsceneonly = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500141 self.invalidate_stamp = False
142 self.dump_signatures = []
143 self.dry_run = False
144 self.tracking = False
145 self.interface = []
146 self.writeeventlog = False
147
148 self.env = {}
149
150 def setConfigParameters(self, parameters):
151 for key in self.__dict__.keys():
152 if key in parameters.options.__dict__:
153 setattr(self, key, parameters.options.__dict__[key])
154 self.env = parameters.environment.copy()
155 self.tracking = parameters.tracking
156
157 def setServerRegIdleCallback(self, srcb):
158 self.server_register_idlecallback = srcb
159
160 def __getstate__(self):
161 state = {}
162 for key in self.__dict__.keys():
163 if key == "server_register_idlecallback":
164 state[key] = None
165 else:
166 state[key] = getattr(self, key)
167 return state
168
169 def __setstate__(self,state):
170 for k in state:
171 setattr(self, k, state[k])
172
173
174def catch_parse_error(func):
175 """Exception handling bits for our parsing"""
176 @wraps(func)
177 def wrapped(fn, *args):
178 try:
179 return func(fn, *args)
180 except IOError as exc:
181 import traceback
182 parselog.critical(traceback.format_exc())
183 parselog.critical("Unable to parse %s: %s" % (fn, exc))
184 sys.exit(1)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500185 except bb.data_smart.ExpansionError as exc:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500186 import traceback
187
188 bbdir = os.path.dirname(__file__) + os.sep
189 exc_class, exc, tb = sys.exc_info()
190 for tb in iter(lambda: tb.tb_next, None):
191 # Skip frames in bitbake itself, we only want the metadata
192 fn, _, _, _ = traceback.extract_tb(tb, 1)[0]
193 if not fn.startswith(bbdir):
194 break
195 parselog.critical("Unable to parse %s", fn, exc_info=(exc_class, exc, tb))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500196 except bb.parse.ParseError as exc:
197 parselog.critical(str(exc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500198 sys.exit(1)
199 return wrapped
200
201@catch_parse_error
202def parse_config_file(fn, data, include=True):
203 return bb.parse.handle(fn, data, include)
204
205@catch_parse_error
206def _inherit(bbclass, data):
207 bb.parse.BBHandler.inherit(bbclass, "configuration INHERITs", 0, data)
208 return data
209
210def findConfigFile(configfile, data):
211 search = []
212 bbpath = data.getVar("BBPATH", True)
213 if bbpath:
214 for i in bbpath.split(":"):
215 search.append(os.path.join(i, "conf", configfile))
216 path = os.getcwd()
217 while path != "/":
218 search.append(os.path.join(path, "conf", configfile))
219 path, _ = os.path.split(path)
220
221 for i in search:
222 if os.path.exists(i):
223 return i
224
225 return None
226
227class CookerDataBuilder(object):
228
229 def __init__(self, cookercfg, worker = False):
230
231 self.prefiles = cookercfg.prefile
232 self.postfiles = cookercfg.postfile
233 self.tracking = cookercfg.tracking
234
235 bb.utils.set_context(bb.utils.clean_context())
236 bb.event.set_class_handlers(bb.event.clean_class_handlers())
237 self.data = bb.data.init()
238 if self.tracking:
239 self.data.enableTracking()
240
241 # Keep a datastore of the initial environment variables and their
242 # values from when BitBake was launched to enable child processes
243 # to use environment variables which have been cleaned from the
244 # BitBake processes env
245 self.savedenv = bb.data.init()
246 for k in cookercfg.env:
247 self.savedenv.setVar(k, cookercfg.env[k])
248
249 filtered_keys = bb.utils.approved_variables()
250 bb.data.inheritFromOS(self.data, self.savedenv, filtered_keys)
251 self.data.setVar("BB_ORIGENV", self.savedenv)
252
253 if worker:
254 self.data.setVar("BB_WORKERCONTEXT", "1")
255
256 def parseBaseConfiguration(self):
257 try:
258 self.parseConfigurationFiles(self.prefiles, self.postfiles)
259 except SyntaxError:
260 raise bb.BBHandledException
261 except bb.data_smart.ExpansionError as e:
262 logger.error(str(e))
263 raise bb.BBHandledException
264 except Exception:
265 logger.exception("Error parsing configuration files")
266 raise bb.BBHandledException
267
268 def _findLayerConf(self, data):
269 return findConfigFile("bblayers.conf", data)
270
271 def parseConfigurationFiles(self, prefiles, postfiles):
272 data = self.data
273 bb.parse.init_parser(data)
274
275 # Parse files for loading *before* bitbake.conf and any includes
276 for f in prefiles:
277 data = parse_config_file(f, data)
278
279 layerconf = self._findLayerConf(data)
280 if layerconf:
281 parselog.debug(2, "Found bblayers.conf (%s)", layerconf)
282 # By definition bblayers.conf is in conf/ of TOPDIR.
283 # We may have been called with cwd somewhere else so reset TOPDIR
284 data.setVar("TOPDIR", os.path.dirname(os.path.dirname(layerconf)))
285 data = parse_config_file(layerconf, data)
286
287 layers = (data.getVar('BBLAYERS', True) or "").split()
288
289 data = bb.data.createCopy(data)
290 approved = bb.utils.approved_variables()
291 for layer in layers:
292 parselog.debug(2, "Adding layer %s", layer)
293 if 'HOME' in approved and '~' in layer:
294 layer = os.path.expanduser(layer)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500295 if layer.endswith('/'):
296 layer = layer.rstrip('/')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500297 data.setVar('LAYERDIR', layer)
298 data = parse_config_file(os.path.join(layer, "conf", "layer.conf"), data)
299 data.expandVarref('LAYERDIR')
300
301 data.delVar('LAYERDIR')
302
303 if not data.getVar("BBPATH", True):
304 msg = "The BBPATH variable is not set"
305 if not layerconf:
306 msg += (" and bitbake did not find a conf/bblayers.conf file in"
307 " the expected location.\nMaybe you accidentally"
308 " invoked bitbake from the wrong directory?")
309 raise SystemExit(msg)
310
311 data = parse_config_file(os.path.join("conf", "bitbake.conf"), data)
312
313 # Parse files for loading *after* bitbake.conf and any includes
314 for p in postfiles:
315 data = parse_config_file(p, data)
316
317 # Handle any INHERITs and inherit the base class
318 bbclasses = ["base"] + (data.getVar('INHERIT', True) or "").split()
319 for bbclass in bbclasses:
320 data = _inherit(bbclass, data)
321
322 # Nomally we only register event handlers at the end of parsing .bb files
323 # We register any handlers we've found so far here...
324 for var in data.getVar('__BBHANDLERS', False) or []:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500325 handlerfn = data.getVarFlag(var, "filename", False)
326 handlerln = int(data.getVarFlag(var, "lineno", False))
327 bb.event.register(var, data.getVar(var, False), (data.getVarFlag(var, "eventmask", True) or "").split(), handlerfn, handlerln)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500328
329 if data.getVar("BB_WORKERCONTEXT", False) is None:
330 bb.fetch.fetcher_init(data)
331 bb.codeparser.parser_cache_init(data)
332 bb.event.fire(bb.event.ConfigParsed(), data)
333
334 if data.getVar("BB_INVALIDCONF", False) is True:
335 data.setVar("BB_INVALIDCONF", False)
336 self.parseConfigurationFiles(self.prefiles, self.postfiles)
337 return
338
339 bb.parse.init_parser(data)
340 data.setVar('BBINCLUDED',bb.parse.get_file_depends(data))
341 self.data = data
342 self.data_hash = data.get_hash()
343
344
345