blob: e30217369635d66637f76d630dd503a1f084ecfa [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
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500222 parser.add_option("", "--setscene-only", help = "Only run setscene tasks, don't run any real tasks.",
223 action = "store_true", dest = "setsceneonly", default = False)
224
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500225 parser.add_option("", "--remote-server", help = "Connect to the specified server.",
226 action = "store", dest = "remote_server", default = False)
227
228 parser.add_option("-m", "--kill-server", help = "Terminate the remote server.",
229 action = "store_true", dest = "kill_server", default = False)
230
231 parser.add_option("", "--observe-only", help = "Connect to a server as an observing-only client.",
232 action = "store_true", dest = "observe_only", default = False)
233
234 parser.add_option("", "--status-only", help = "Check the status of the remote bitbake server.",
235 action = "store_true", dest = "status_only", default = False)
236
237 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.",
238 action = "store", dest = "writeeventlog")
239
240 options, targets = parser.parse_args(argv)
241
242 # some environmental variables set also configuration options
243 if "BBSERVER" in os.environ:
244 options.servertype = "xmlrpc"
245 options.remote_server = os.environ["BBSERVER"]
246
247 if "BBTOKEN" in os.environ:
248 options.xmlrpctoken = os.environ["BBTOKEN"]
249
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500250 if "BBEVENTLOG" in os.environ:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500251 options.writeeventlog = os.environ["BBEVENTLOG"]
252
253 # fill in proper log name if not supplied
254 if options.writeeventlog is not None and len(options.writeeventlog) == 0:
255 import datetime
256 options.writeeventlog = "bitbake_eventlog_%s.json" % datetime.datetime.now().strftime("%Y%m%d%H%M%S")
257
258 # if BBSERVER says to autodetect, let's do that
259 if options.remote_server:
260 [host, port] = options.remote_server.split(":", 2)
261 port = int(port)
262 # use automatic port if port set to -1, means read it from
263 # the bitbake.lock file; this is a bit tricky, but we always expect
264 # to be in the base of the build directory if we need to have a
265 # chance to start the server later, anyway
266 if port == -1:
267 lock_location = "./bitbake.lock"
268 # we try to read the address at all times; if the server is not started,
269 # we'll try to start it after the first connect fails, below
270 try:
271 lf = open(lock_location, 'r')
272 remotedef = lf.readline()
273 [host, port] = remotedef.split(":")
274 port = int(port)
275 lf.close()
276 options.remote_server = remotedef
277 except Exception as e:
278 raise BBMainException("Failed to read bitbake.lock (%s), invalid port" % str(e))
279
280 return options, targets[1:]
281
282
283def start_server(servermodule, configParams, configuration, features):
284 server = servermodule.BitBakeServer()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500285 single_use = not configParams.server_only
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500286 if configParams.bind:
287 (host, port) = configParams.bind.split(':')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500288 server.initServer((host, int(port)), single_use)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500289 configuration.interface = [ server.serverImpl.host, server.serverImpl.port ]
290 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500291 server.initServer(single_use=single_use)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500292 configuration.interface = []
293
294 try:
295 configuration.setServerRegIdleCallback(server.getServerIdleCB())
296
297 cooker = bb.cooker.BBCooker(configuration, features)
298
299 server.addcooker(cooker)
300 server.saveConnectionDetails()
301 except Exception as e:
302 exc_info = sys.exc_info()
303 while hasattr(server, "event_queue"):
304 try:
305 import queue
306 except ImportError:
307 import Queue as queue
308 try:
309 event = server.event_queue.get(block=False)
310 except (queue.Empty, IOError):
311 break
312 if isinstance(event, logging.LogRecord):
313 logger.handle(event)
314 raise exc_info[1], None, exc_info[2]
315 server.detach()
316 cooker.lock.close()
317 return server
318
319
320def bitbake_main(configParams, configuration):
321
322 # Python multiprocessing requires /dev/shm on Linux
323 if sys.platform.startswith('linux') and not os.access('/dev/shm', os.W_OK | os.X_OK):
324 raise BBMainException("FATAL: /dev/shm does not exist or is not writable")
325
326 # Unbuffer stdout to avoid log truncation in the event
327 # of an unorderly exit as well as to provide timely
328 # updates to log files for use with tail
329 try:
330 if sys.stdout.name == '<stdout>':
331 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
332 except:
333 pass
334
335
336 configuration.setConfigParameters(configParams)
337
338 ui_module = import_extension_module(bb.ui, configParams.ui, 'main')
339 servermodule = import_extension_module(bb.server, configParams.servertype, 'BitBakeServer')
340
341 if configParams.server_only:
342 if configParams.servertype != "xmlrpc":
343 raise BBMainException("FATAL: If '--server-only' is defined, we must set the "
344 "servertype as 'xmlrpc'.\n")
345 if not configParams.bind:
346 raise BBMainException("FATAL: The '--server-only' option requires a name/address "
347 "to bind to with the -B option.\n")
348 if configParams.remote_server:
349 raise BBMainException("FATAL: The '--server-only' option conflicts with %s.\n" %
350 ("the BBSERVER environment variable" if "BBSERVER" in os.environ \
351 else "the '--remote-server' option" ))
352
353 if configParams.bind and configParams.servertype != "xmlrpc":
354 raise BBMainException("FATAL: If '-B' or '--bind' is defined, we must "
355 "set the servertype as 'xmlrpc'.\n")
356
357 if configParams.remote_server and configParams.servertype != "xmlrpc":
358 raise BBMainException("FATAL: If '--remote-server' is defined, we must "
359 "set the servertype as 'xmlrpc'.\n")
360
361 if configParams.observe_only and (not configParams.remote_server or configParams.bind):
362 raise BBMainException("FATAL: '--observe-only' can only be used by UI clients "
363 "connecting to a server.\n")
364
365 if configParams.kill_server and not configParams.remote_server:
366 raise BBMainException("FATAL: '--kill-server' can only be used to terminate a remote server")
367
368 if "BBDEBUG" in os.environ:
369 level = int(os.environ["BBDEBUG"])
370 if level > configuration.debug:
371 configuration.debug = level
372
373 bb.msg.init_msgconfig(configParams.verbose, configuration.debug,
374 configuration.debug_domains)
375
376 # Ensure logging messages get sent to the UI as events
377 handler = bb.event.LogHandler()
378 if not configParams.status_only:
379 # In status only mode there are no logs and no UI
380 logger.addHandler(handler)
381
382 # Clear away any spurious environment variables while we stoke up the cooker
383 cleanedvars = bb.utils.clean_environment()
384
385 featureset = []
386 if not configParams.server_only:
387 # Collect the feature set for the UI
388 featureset = getattr(ui_module, "featureSet", [])
389
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500390 if configParams.server_only:
391 for param in ('prefile', 'postfile'):
392 value = getattr(configParams, param)
393 if value:
394 setattr(configuration, "%s_server" % param, value)
395 param = "%s_server" % param
396
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500397 if not configParams.remote_server:
398 # we start a server with a given configuration
399 server = start_server(servermodule, configParams, configuration, featureset)
400 bb.event.ui_queue = []
401 else:
402 # we start a stub server that is actually a XMLRPClient that connects to a real server
403 server = servermodule.BitBakeXMLRPCClient(configParams.observe_only, configParams.xmlrpctoken)
404 server.saveConnectionDetails(configParams.remote_server)
405
406
407 if not configParams.server_only:
408 try:
409 server_connection = server.establishConnection(featureset)
410 except Exception as e:
411 bb.fatal("Could not connect to server %s: %s" % (configParams.remote_server, str(e)))
412
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500413 if configParams.kill_server:
414 server_connection.connection.terminateServer()
415 bb.event.ui_queue = []
416 return 0
417
418 server_connection.setupEventQueue()
419
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420 # Restore the environment in case the UI needs it
421 for k in cleanedvars:
422 os.environ[k] = cleanedvars[k]
423
424 logger.removeHandler(handler)
425
426
427 if configParams.status_only:
428 server_connection.terminate()
429 return 0
430
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500431 try:
432 return ui_module.main(server_connection.connection, server_connection.events, configParams)
433 finally:
434 bb.event.ui_queue = []
435 server_connection.terminate()
436 else:
437 print("Bitbake server address: %s, server port: %s" % (server.serverImpl.host, server.serverImpl.port))
438 return 0
439
440 return 1