blob: 8762f7220acd564b78d24cd842ef3bbefb0bdf02 [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
26import sys
27import logging
28import optparse
29import warnings
30
31import bb
32from bb import event
33import bb.msg
34from bb import cooker
35from bb import ui
36from bb import server
37from bb import cookerdata
38
39logger = logging.getLogger("BitBake")
40
41class BBMainException(Exception):
42 pass
43
44def present_options(optionlist):
45 if len(optionlist) > 1:
46 return ' or '.join([', '.join(optionlist[:-1]), optionlist[-1]])
47 else:
48 return optionlist[0]
49
50class BitbakeHelpFormatter(optparse.IndentedHelpFormatter):
51 def format_option(self, option):
52 # We need to do this here rather than in the text we supply to
53 # add_option() because we don't want to call list_extension_modules()
54 # on every execution (since it imports all of the modules)
55 # Note also that we modify option.help rather than the returned text
56 # - this is so that we don't have to re-format the text ourselves
57 if option.dest == 'ui':
58 valid_uis = list_extension_modules(bb.ui, 'main')
59 option.help = option.help.replace('@CHOICES@', present_options(valid_uis))
60 elif option.dest == 'servertype':
61 valid_server_types = list_extension_modules(bb.server, 'BitBakeServer')
62 option.help = option.help.replace('@CHOICES@', present_options(valid_server_types))
63
64 return optparse.IndentedHelpFormatter.format_option(self, option)
65
66def list_extension_modules(pkg, checkattr):
67 """
68 Lists extension modules in a specific Python package
69 (e.g. UIs, servers). NOTE: Calling this function will import all of the
70 submodules of the specified module in order to check for the specified
71 attribute; this can have unusual side-effects. As a result, this should
72 only be called when displaying help text or error messages.
73 Parameters:
74 pkg: previously imported Python package to list
75 checkattr: attribute to look for in module to determine if it's valid
76 as the type of extension you are looking for
77 """
78 import pkgutil
79 pkgdir = os.path.dirname(pkg.__file__)
80
81 modules = []
82 for _, modulename, _ in pkgutil.iter_modules([pkgdir]):
83 if os.path.isdir(os.path.join(pkgdir, modulename)):
84 # ignore directories
85 continue
86 try:
87 module = __import__(pkg.__name__, fromlist=[modulename])
88 except:
89 # If we can't import it, it's not valid
90 continue
91 module_if = getattr(module, modulename)
92 if getattr(module_if, 'hidden_extension', False):
93 continue
94 if not checkattr or hasattr(module_if, checkattr):
95 modules.append(modulename)
96 return modules
97
98def import_extension_module(pkg, modulename, checkattr):
99 try:
100 # Dynamically load the UI based on the ui name. Although we
101 # suggest a fixed set this allows you to have flexibility in which
102 # ones are available.
103 module = __import__(pkg.__name__, fromlist = [modulename])
104 return getattr(module, modulename)
105 except AttributeError:
106 raise BBMainException('FATAL: Unable to import extension module "%s" from %s. Valid extension modules: %s' % (modulename, pkg.__name__, present_options(list_extension_modules(pkg, checkattr))))
107
108
109# Display bitbake/OE warnings via the BitBake.Warnings logger, ignoring others"""
110warnlog = logging.getLogger("BitBake.Warnings")
111_warnings_showwarning = warnings.showwarning
112def _showwarning(message, category, filename, lineno, file=None, line=None):
113 if file is not None:
114 if _warnings_showwarning is not None:
115 _warnings_showwarning(message, category, filename, lineno, file, line)
116 else:
117 s = warnings.formatwarning(message, category, filename, lineno)
118 warnlog.warn(s)
119
120warnings.showwarning = _showwarning
121warnings.filterwarnings("ignore")
122warnings.filterwarnings("default", module="(<string>$|(oe|bb)\.)")
123warnings.filterwarnings("ignore", category=PendingDeprecationWarning)
124warnings.filterwarnings("ignore", category=ImportWarning)
125warnings.filterwarnings("ignore", category=DeprecationWarning, module="<string>$")
126warnings.filterwarnings("ignore", message="With-statements now directly support multiple context managers")
127
128class BitBakeConfigParameters(cookerdata.ConfigParameters):
129
130 def parseCommandLine(self, argv=sys.argv):
131 parser = optparse.OptionParser(
132 formatter = BitbakeHelpFormatter(),
133 version = "BitBake Build Tool Core version %s" % bb.__version__,
134 usage = """%prog [options] [recipename/target recipe:do_task ...]
135
136 Executes the specified task (default is 'build') for a given set of target recipes (.bb files).
137 It is assumed there is a conf/bblayers.conf available in cwd or in BBPATH which
138 will provide the layer, BBFILES and other configuration information.""")
139
140 parser.add_option("-b", "--buildfile", help = "Execute tasks from a specific .bb recipe directly. WARNING: Does not handle any dependencies from other recipes.",
141 action = "store", dest = "buildfile", default = None)
142
143 parser.add_option("-k", "--continue", help = "Continue as much as possible after an error. While the target that failed and anything depending on it cannot be built, as much as possible will be built before stopping.",
144 action = "store_false", dest = "abort", default = True)
145
146 parser.add_option("-a", "--tryaltconfigs", help = "Continue with builds by trying to use alternative providers where possible.",
147 action = "store_true", dest = "tryaltconfigs", default = False)
148
149 parser.add_option("-f", "--force", help = "Force the specified targets/task to run (invalidating any existing stamp file).",
150 action = "store_true", dest = "force", default = False)
151
152 parser.add_option("-c", "--cmd", help = "Specify the task to execute. The exact options available depend on the metadata. Some examples might be 'compile' or 'populate_sysroot' or 'listtasks' may give a list of the tasks available.",
153 action = "store", dest = "cmd")
154
155 parser.add_option("-C", "--clear-stamp", help = "Invalidate the stamp for the specified task such as 'compile' and then run the default task for the specified target(s).",
156 action = "store", dest = "invalidate_stamp")
157
158 parser.add_option("-r", "--read", help = "Read the specified file before bitbake.conf.",
159 action = "append", dest = "prefile", default = [])
160
161 parser.add_option("-R", "--postread", help = "Read the specified file after bitbake.conf.",
162 action = "append", dest = "postfile", default = [])
163
164 parser.add_option("-v", "--verbose", help = "Output more log message data to the terminal.",
165 action = "store_true", dest = "verbose", default = False)
166
167 parser.add_option("-D", "--debug", help = "Increase the debug level. You can specify this more than once.",
168 action = "count", dest="debug", default = 0)
169
170 parser.add_option("-n", "--dry-run", help = "Don't execute, just go through the motions.",
171 action = "store_true", dest = "dry_run", default = False)
172
173 parser.add_option("-S", "--dump-signatures", help = "Dump out the signature construction information, with no task execution. The SIGNATURE_HANDLER parameter is passed to the handler. Two common values are none and printdiff but the handler may define more/less. none means only dump the signature, printdiff means compare the dumped signature with the cached one.",
174 action = "append", dest = "dump_signatures", default = [], metavar="SIGNATURE_HANDLER")
175
176 parser.add_option("-p", "--parse-only", help = "Quit after parsing the BB recipes.",
177 action = "store_true", dest = "parse_only", default = False)
178
179 parser.add_option("-s", "--show-versions", help = "Show current and preferred versions of all recipes.",
180 action = "store_true", dest = "show_versions", default = False)
181
182 parser.add_option("-e", "--environment", help = "Show the global or per-recipe environment complete with information about where variables were set/changed.",
183 action = "store_true", dest = "show_environment", default = False)
184
185 parser.add_option("-g", "--graphviz", help = "Save dependency tree information for the specified targets in the dot syntax.",
186 action = "store_true", dest = "dot_graph", default = False)
187
188 parser.add_option("-I", "--ignore-deps", help = """Assume these dependencies don't exist and are already provided (equivalent to ASSUME_PROVIDED). Useful to make dependency graphs more appealing""",
189 action = "append", dest = "extra_assume_provided", default = [])
190
191 parser.add_option("-l", "--log-domains", help = """Show debug logging for the specified logging domains""",
192 action = "append", dest = "debug_domains", default = [])
193
194 parser.add_option("-P", "--profile", help = "Profile the command and save reports.",
195 action = "store_true", dest = "profile", default = False)
196
197 env_ui = os.environ.get('BITBAKE_UI', None)
198 default_ui = env_ui or 'knotty'
199 # @CHOICES@ is substituted out by BitbakeHelpFormatter above
200 parser.add_option("-u", "--ui", help = "The user interface to use (@CHOICES@ - default %default).",
201 action="store", dest="ui", default=default_ui)
202
203 # @CHOICES@ is substituted out by BitbakeHelpFormatter above
204 parser.add_option("-t", "--servertype", help = "Choose which server type to use (@CHOICES@ - default %default).",
205 action = "store", dest = "servertype", default = "process")
206
207 parser.add_option("", "--token", help = "Specify the connection token to be used when connecting to a remote server.",
208 action = "store", dest = "xmlrpctoken")
209
210 parser.add_option("", "--revisions-changed", help = "Set the exit code depending on whether upstream floating revisions have changed or not.",
211 action = "store_true", dest = "revisions_changed", default = False)
212
213 parser.add_option("", "--server-only", help = "Run bitbake without a UI, only starting a server (cooker) process.",
214 action = "store_true", dest = "server_only", default = False)
215
216 parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to.",
217 action = "store", dest = "bind", default = False)
218
219 parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks. sstate will be ignored and everything needed, built.",
220 action = "store_true", dest = "nosetscene", default = False)
221
222 parser.add_option("", "--remote-server", help = "Connect to the specified server.",
223 action = "store", dest = "remote_server", default = False)
224
225 parser.add_option("-m", "--kill-server", help = "Terminate the remote server.",
226 action = "store_true", dest = "kill_server", default = False)
227
228 parser.add_option("", "--observe-only", help = "Connect to a server as an observing-only client.",
229 action = "store_true", dest = "observe_only", default = False)
230
231 parser.add_option("", "--status-only", help = "Check the status of the remote bitbake server.",
232 action = "store_true", dest = "status_only", default = False)
233
234 parser.add_option("-w", "--write-log", help = "Writes the event log of the build to a bitbake event json file. Use '' (empty string) to assign the name automatically.",
235 action = "store", dest = "writeeventlog")
236
237 options, targets = parser.parse_args(argv)
238
239 # some environmental variables set also configuration options
240 if "BBSERVER" in os.environ:
241 options.servertype = "xmlrpc"
242 options.remote_server = os.environ["BBSERVER"]
243
244 if "BBTOKEN" in os.environ:
245 options.xmlrpctoken = os.environ["BBTOKEN"]
246
247 if "BBEVENTLOG" is os.environ:
248 options.writeeventlog = os.environ["BBEVENTLOG"]
249
250 # fill in proper log name if not supplied
251 if options.writeeventlog is not None and len(options.writeeventlog) == 0:
252 import datetime
253 options.writeeventlog = "bitbake_eventlog_%s.json" % datetime.datetime.now().strftime("%Y%m%d%H%M%S")
254
255 # if BBSERVER says to autodetect, let's do that
256 if options.remote_server:
257 [host, port] = options.remote_server.split(":", 2)
258 port = int(port)
259 # use automatic port if port set to -1, means read it from
260 # the bitbake.lock file; this is a bit tricky, but we always expect
261 # to be in the base of the build directory if we need to have a
262 # chance to start the server later, anyway
263 if port == -1:
264 lock_location = "./bitbake.lock"
265 # we try to read the address at all times; if the server is not started,
266 # we'll try to start it after the first connect fails, below
267 try:
268 lf = open(lock_location, 'r')
269 remotedef = lf.readline()
270 [host, port] = remotedef.split(":")
271 port = int(port)
272 lf.close()
273 options.remote_server = remotedef
274 except Exception as e:
275 raise BBMainException("Failed to read bitbake.lock (%s), invalid port" % str(e))
276
277 return options, targets[1:]
278
279
280def start_server(servermodule, configParams, configuration, features):
281 server = servermodule.BitBakeServer()
282 if configParams.bind:
283 (host, port) = configParams.bind.split(':')
284 server.initServer((host, int(port)))
285 configuration.interface = [ server.serverImpl.host, server.serverImpl.port ]
286 else:
287 server.initServer()
288 configuration.interface = []
289
290 try:
291 configuration.setServerRegIdleCallback(server.getServerIdleCB())
292
293 cooker = bb.cooker.BBCooker(configuration, features)
294
295 server.addcooker(cooker)
296 server.saveConnectionDetails()
297 except Exception as e:
298 exc_info = sys.exc_info()
299 while hasattr(server, "event_queue"):
300 try:
301 import queue
302 except ImportError:
303 import Queue as queue
304 try:
305 event = server.event_queue.get(block=False)
306 except (queue.Empty, IOError):
307 break
308 if isinstance(event, logging.LogRecord):
309 logger.handle(event)
310 raise exc_info[1], None, exc_info[2]
311 server.detach()
312 cooker.lock.close()
313 return server
314
315
316def bitbake_main(configParams, configuration):
317
318 # Python multiprocessing requires /dev/shm on Linux
319 if sys.platform.startswith('linux') and not os.access('/dev/shm', os.W_OK | os.X_OK):
320 raise BBMainException("FATAL: /dev/shm does not exist or is not writable")
321
322 # Unbuffer stdout to avoid log truncation in the event
323 # of an unorderly exit as well as to provide timely
324 # updates to log files for use with tail
325 try:
326 if sys.stdout.name == '<stdout>':
327 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
328 except:
329 pass
330
331
332 configuration.setConfigParameters(configParams)
333
334 ui_module = import_extension_module(bb.ui, configParams.ui, 'main')
335 servermodule = import_extension_module(bb.server, configParams.servertype, 'BitBakeServer')
336
337 if configParams.server_only:
338 if configParams.servertype != "xmlrpc":
339 raise BBMainException("FATAL: If '--server-only' is defined, we must set the "
340 "servertype as 'xmlrpc'.\n")
341 if not configParams.bind:
342 raise BBMainException("FATAL: The '--server-only' option requires a name/address "
343 "to bind to with the -B option.\n")
344 if configParams.remote_server:
345 raise BBMainException("FATAL: The '--server-only' option conflicts with %s.\n" %
346 ("the BBSERVER environment variable" if "BBSERVER" in os.environ \
347 else "the '--remote-server' option" ))
348
349 if configParams.bind and configParams.servertype != "xmlrpc":
350 raise BBMainException("FATAL: If '-B' or '--bind' is defined, we must "
351 "set the servertype as 'xmlrpc'.\n")
352
353 if configParams.remote_server and configParams.servertype != "xmlrpc":
354 raise BBMainException("FATAL: If '--remote-server' is defined, we must "
355 "set the servertype as 'xmlrpc'.\n")
356
357 if configParams.observe_only and (not configParams.remote_server or configParams.bind):
358 raise BBMainException("FATAL: '--observe-only' can only be used by UI clients "
359 "connecting to a server.\n")
360
361 if configParams.kill_server and not configParams.remote_server:
362 raise BBMainException("FATAL: '--kill-server' can only be used to terminate a remote server")
363
364 if "BBDEBUG" in os.environ:
365 level = int(os.environ["BBDEBUG"])
366 if level > configuration.debug:
367 configuration.debug = level
368
369 bb.msg.init_msgconfig(configParams.verbose, configuration.debug,
370 configuration.debug_domains)
371
372 # Ensure logging messages get sent to the UI as events
373 handler = bb.event.LogHandler()
374 if not configParams.status_only:
375 # In status only mode there are no logs and no UI
376 logger.addHandler(handler)
377
378 # Clear away any spurious environment variables while we stoke up the cooker
379 cleanedvars = bb.utils.clean_environment()
380
381 featureset = []
382 if not configParams.server_only:
383 # Collect the feature set for the UI
384 featureset = getattr(ui_module, "featureSet", [])
385
386 if not configParams.remote_server:
387 # we start a server with a given configuration
388 server = start_server(servermodule, configParams, configuration, featureset)
389 bb.event.ui_queue = []
390 else:
391 # we start a stub server that is actually a XMLRPClient that connects to a real server
392 server = servermodule.BitBakeXMLRPCClient(configParams.observe_only, configParams.xmlrpctoken)
393 server.saveConnectionDetails(configParams.remote_server)
394
395
396 if not configParams.server_only:
397 try:
398 server_connection = server.establishConnection(featureset)
399 except Exception as e:
400 bb.fatal("Could not connect to server %s: %s" % (configParams.remote_server, str(e)))
401
402 # Restore the environment in case the UI needs it
403 for k in cleanedvars:
404 os.environ[k] = cleanedvars[k]
405
406 logger.removeHandler(handler)
407
408
409 if configParams.status_only:
410 server_connection.terminate()
411 return 0
412
413 if configParams.kill_server:
414 server_connection.connection.terminateServer()
415 bb.event.ui_queue = []
416 return 0
417
418 try:
419 return ui_module.main(server_connection.connection, server_connection.events, configParams)
420 finally:
421 bb.event.ui_queue = []
422 server_connection.terminate()
423 else:
424 print("Bitbake server address: %s, server port: %s" % (server.serverImpl.host, server.serverImpl.port))
425 return 0
426
427 return 1