1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# Copyright (C) 2003, 2004 Chris Larson 3*4882a593Smuzhiyun# Copyright (C) 2003, 2004 Phil Blundell 4*4882a593Smuzhiyun# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer 5*4882a593Smuzhiyun# Copyright (C) 2005 Holger Hans Peter Freyther 6*4882a593Smuzhiyun# Copyright (C) 2005 ROAD GmbH 7*4882a593Smuzhiyun# Copyright (C) 2006 Richard Purdie 8*4882a593Smuzhiyun# 9*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 10*4882a593Smuzhiyun# 11*4882a593Smuzhiyun 12*4882a593Smuzhiyunimport os 13*4882a593Smuzhiyunimport sys 14*4882a593Smuzhiyunimport logging 15*4882a593Smuzhiyunimport optparse 16*4882a593Smuzhiyunimport warnings 17*4882a593Smuzhiyunimport fcntl 18*4882a593Smuzhiyunimport time 19*4882a593Smuzhiyunimport traceback 20*4882a593Smuzhiyun 21*4882a593Smuzhiyunimport bb 22*4882a593Smuzhiyunfrom bb import event 23*4882a593Smuzhiyunimport bb.msg 24*4882a593Smuzhiyunfrom bb import cooker 25*4882a593Smuzhiyunfrom bb import ui 26*4882a593Smuzhiyunfrom bb import server 27*4882a593Smuzhiyunfrom bb import cookerdata 28*4882a593Smuzhiyun 29*4882a593Smuzhiyunimport bb.server.process 30*4882a593Smuzhiyunimport bb.server.xmlrpcclient 31*4882a593Smuzhiyun 32*4882a593Smuzhiyunlogger = logging.getLogger("BitBake") 33*4882a593Smuzhiyun 34*4882a593Smuzhiyunclass BBMainException(Exception): 35*4882a593Smuzhiyun pass 36*4882a593Smuzhiyun 37*4882a593Smuzhiyunclass BBMainFatal(bb.BBHandledException): 38*4882a593Smuzhiyun pass 39*4882a593Smuzhiyun 40*4882a593Smuzhiyundef present_options(optionlist): 41*4882a593Smuzhiyun if len(optionlist) > 1: 42*4882a593Smuzhiyun return ' or '.join([', '.join(optionlist[:-1]), optionlist[-1]]) 43*4882a593Smuzhiyun else: 44*4882a593Smuzhiyun return optionlist[0] 45*4882a593Smuzhiyun 46*4882a593Smuzhiyunclass BitbakeHelpFormatter(optparse.IndentedHelpFormatter): 47*4882a593Smuzhiyun def format_option(self, option): 48*4882a593Smuzhiyun # We need to do this here rather than in the text we supply to 49*4882a593Smuzhiyun # add_option() because we don't want to call list_extension_modules() 50*4882a593Smuzhiyun # on every execution (since it imports all of the modules) 51*4882a593Smuzhiyun # Note also that we modify option.help rather than the returned text 52*4882a593Smuzhiyun # - this is so that we don't have to re-format the text ourselves 53*4882a593Smuzhiyun if option.dest == 'ui': 54*4882a593Smuzhiyun valid_uis = list_extension_modules(bb.ui, 'main') 55*4882a593Smuzhiyun option.help = option.help.replace('@CHOICES@', present_options(valid_uis)) 56*4882a593Smuzhiyun 57*4882a593Smuzhiyun return optparse.IndentedHelpFormatter.format_option(self, option) 58*4882a593Smuzhiyun 59*4882a593Smuzhiyundef list_extension_modules(pkg, checkattr): 60*4882a593Smuzhiyun """ 61*4882a593Smuzhiyun Lists extension modules in a specific Python package 62*4882a593Smuzhiyun (e.g. UIs, servers). NOTE: Calling this function will import all of the 63*4882a593Smuzhiyun submodules of the specified module in order to check for the specified 64*4882a593Smuzhiyun attribute; this can have unusual side-effects. As a result, this should 65*4882a593Smuzhiyun only be called when displaying help text or error messages. 66*4882a593Smuzhiyun Parameters: 67*4882a593Smuzhiyun pkg: previously imported Python package to list 68*4882a593Smuzhiyun checkattr: attribute to look for in module to determine if it's valid 69*4882a593Smuzhiyun as the type of extension you are looking for 70*4882a593Smuzhiyun """ 71*4882a593Smuzhiyun import pkgutil 72*4882a593Smuzhiyun pkgdir = os.path.dirname(pkg.__file__) 73*4882a593Smuzhiyun 74*4882a593Smuzhiyun modules = [] 75*4882a593Smuzhiyun for _, modulename, _ in pkgutil.iter_modules([pkgdir]): 76*4882a593Smuzhiyun if os.path.isdir(os.path.join(pkgdir, modulename)): 77*4882a593Smuzhiyun # ignore directories 78*4882a593Smuzhiyun continue 79*4882a593Smuzhiyun try: 80*4882a593Smuzhiyun module = __import__(pkg.__name__, fromlist=[modulename]) 81*4882a593Smuzhiyun except: 82*4882a593Smuzhiyun # If we can't import it, it's not valid 83*4882a593Smuzhiyun continue 84*4882a593Smuzhiyun module_if = getattr(module, modulename) 85*4882a593Smuzhiyun if getattr(module_if, 'hidden_extension', False): 86*4882a593Smuzhiyun continue 87*4882a593Smuzhiyun if not checkattr or hasattr(module_if, checkattr): 88*4882a593Smuzhiyun modules.append(modulename) 89*4882a593Smuzhiyun return modules 90*4882a593Smuzhiyun 91*4882a593Smuzhiyundef import_extension_module(pkg, modulename, checkattr): 92*4882a593Smuzhiyun try: 93*4882a593Smuzhiyun # Dynamically load the UI based on the ui name. Although we 94*4882a593Smuzhiyun # suggest a fixed set this allows you to have flexibility in which 95*4882a593Smuzhiyun # ones are available. 96*4882a593Smuzhiyun module = __import__(pkg.__name__, fromlist=[modulename]) 97*4882a593Smuzhiyun return getattr(module, modulename) 98*4882a593Smuzhiyun except AttributeError: 99*4882a593Smuzhiyun modules = present_options(list_extension_modules(pkg, checkattr)) 100*4882a593Smuzhiyun raise BBMainException('FATAL: Unable to import extension module "%s" from %s. ' 101*4882a593Smuzhiyun 'Valid extension modules: %s' % (modulename, pkg.__name__, modules)) 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun# Display bitbake/OE warnings via the BitBake.Warnings logger, ignoring others""" 104*4882a593Smuzhiyunwarnlog = logging.getLogger("BitBake.Warnings") 105*4882a593Smuzhiyun_warnings_showwarning = warnings.showwarning 106*4882a593Smuzhiyundef _showwarning(message, category, filename, lineno, file=None, line=None): 107*4882a593Smuzhiyun if file is not None: 108*4882a593Smuzhiyun if _warnings_showwarning is not None: 109*4882a593Smuzhiyun _warnings_showwarning(message, category, filename, lineno, file, line) 110*4882a593Smuzhiyun else: 111*4882a593Smuzhiyun s = warnings.formatwarning(message, category, filename, lineno) 112*4882a593Smuzhiyun warnlog.warning(s) 113*4882a593Smuzhiyun 114*4882a593Smuzhiyunwarnings.showwarning = _showwarning 115*4882a593Smuzhiyun 116*4882a593Smuzhiyundef create_bitbake_parser(): 117*4882a593Smuzhiyun parser = optparse.OptionParser( 118*4882a593Smuzhiyun formatter=BitbakeHelpFormatter(), 119*4882a593Smuzhiyun version="BitBake Build Tool Core version %s" % bb.__version__, 120*4882a593Smuzhiyun usage="""%prog [options] [recipename/target recipe:do_task ...] 121*4882a593Smuzhiyun 122*4882a593Smuzhiyun Executes the specified task (default is 'build') for a given set of target recipes (.bb files). 123*4882a593Smuzhiyun It is assumed there is a conf/bblayers.conf available in cwd or in BBPATH which 124*4882a593Smuzhiyun will provide the layer, BBFILES and other configuration information.""") 125*4882a593Smuzhiyun 126*4882a593Smuzhiyun parser.add_option("-b", "--buildfile", action="store", dest="buildfile", default=None, 127*4882a593Smuzhiyun help="Execute tasks from a specific .bb recipe directly. WARNING: Does " 128*4882a593Smuzhiyun "not handle any dependencies from other recipes.") 129*4882a593Smuzhiyun 130*4882a593Smuzhiyun parser.add_option("-k", "--continue", action="store_false", dest="halt", default=True, 131*4882a593Smuzhiyun help="Continue as much as possible after an error. While the target that " 132*4882a593Smuzhiyun "failed and anything depending on it cannot be built, as much as " 133*4882a593Smuzhiyun "possible will be built before stopping.") 134*4882a593Smuzhiyun 135*4882a593Smuzhiyun parser.add_option("-f", "--force", action="store_true", dest="force", default=False, 136*4882a593Smuzhiyun help="Force the specified targets/task to run (invalidating any " 137*4882a593Smuzhiyun "existing stamp file).") 138*4882a593Smuzhiyun 139*4882a593Smuzhiyun parser.add_option("-c", "--cmd", action="store", dest="cmd", 140*4882a593Smuzhiyun help="Specify the task to execute. The exact options available " 141*4882a593Smuzhiyun "depend on the metadata. Some examples might be 'compile'" 142*4882a593Smuzhiyun " or 'populate_sysroot' or 'listtasks' may give a list of " 143*4882a593Smuzhiyun "the tasks available.") 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun parser.add_option("-C", "--clear-stamp", action="store", dest="invalidate_stamp", 146*4882a593Smuzhiyun help="Invalidate the stamp for the specified task such as 'compile' " 147*4882a593Smuzhiyun "and then run the default task for the specified target(s).") 148*4882a593Smuzhiyun 149*4882a593Smuzhiyun parser.add_option("-r", "--read", action="append", dest="prefile", default=[], 150*4882a593Smuzhiyun help="Read the specified file before bitbake.conf.") 151*4882a593Smuzhiyun 152*4882a593Smuzhiyun parser.add_option("-R", "--postread", action="append", dest="postfile", default=[], 153*4882a593Smuzhiyun help="Read the specified file after bitbake.conf.") 154*4882a593Smuzhiyun 155*4882a593Smuzhiyun parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, 156*4882a593Smuzhiyun help="Enable tracing of shell tasks (with 'set -x'). " 157*4882a593Smuzhiyun "Also print bb.note(...) messages to stdout (in " 158*4882a593Smuzhiyun "addition to writing them to ${T}/log.do_<task>).") 159*4882a593Smuzhiyun 160*4882a593Smuzhiyun parser.add_option("-D", "--debug", action="count", dest="debug", default=0, 161*4882a593Smuzhiyun help="Increase the debug level. You can specify this " 162*4882a593Smuzhiyun "more than once. -D sets the debug level to 1, " 163*4882a593Smuzhiyun "where only bb.debug(1, ...) messages are printed " 164*4882a593Smuzhiyun "to stdout; -DD sets the debug level to 2, where " 165*4882a593Smuzhiyun "both bb.debug(1, ...) and bb.debug(2, ...) " 166*4882a593Smuzhiyun "messages are printed; etc. Without -D, no debug " 167*4882a593Smuzhiyun "messages are printed. Note that -D only affects " 168*4882a593Smuzhiyun "output to stdout. All debug messages are written " 169*4882a593Smuzhiyun "to ${T}/log.do_taskname, regardless of the debug " 170*4882a593Smuzhiyun "level.") 171*4882a593Smuzhiyun 172*4882a593Smuzhiyun parser.add_option("-q", "--quiet", action="count", dest="quiet", default=0, 173*4882a593Smuzhiyun help="Output less log message data to the terminal. You can specify this more than once.") 174*4882a593Smuzhiyun 175*4882a593Smuzhiyun parser.add_option("-n", "--dry-run", action="store_true", dest="dry_run", default=False, 176*4882a593Smuzhiyun help="Don't execute, just go through the motions.") 177*4882a593Smuzhiyun 178*4882a593Smuzhiyun parser.add_option("-S", "--dump-signatures", action="append", dest="dump_signatures", 179*4882a593Smuzhiyun default=[], metavar="SIGNATURE_HANDLER", 180*4882a593Smuzhiyun help="Dump out the signature construction information, with no task " 181*4882a593Smuzhiyun "execution. The SIGNATURE_HANDLER parameter is passed to the " 182*4882a593Smuzhiyun "handler. Two common values are none and printdiff but the handler " 183*4882a593Smuzhiyun "may define more/less. none means only dump the signature, printdiff" 184*4882a593Smuzhiyun " means compare the dumped signature with the cached one.") 185*4882a593Smuzhiyun 186*4882a593Smuzhiyun parser.add_option("-p", "--parse-only", action="store_true", 187*4882a593Smuzhiyun dest="parse_only", default=False, 188*4882a593Smuzhiyun help="Quit after parsing the BB recipes.") 189*4882a593Smuzhiyun 190*4882a593Smuzhiyun parser.add_option("-s", "--show-versions", action="store_true", 191*4882a593Smuzhiyun dest="show_versions", default=False, 192*4882a593Smuzhiyun help="Show current and preferred versions of all recipes.") 193*4882a593Smuzhiyun 194*4882a593Smuzhiyun parser.add_option("-e", "--environment", action="store_true", 195*4882a593Smuzhiyun dest="show_environment", default=False, 196*4882a593Smuzhiyun help="Show the global or per-recipe environment complete with information" 197*4882a593Smuzhiyun " about where variables were set/changed.") 198*4882a593Smuzhiyun 199*4882a593Smuzhiyun parser.add_option("-g", "--graphviz", action="store_true", dest="dot_graph", default=False, 200*4882a593Smuzhiyun help="Save dependency tree information for the specified " 201*4882a593Smuzhiyun "targets in the dot syntax.") 202*4882a593Smuzhiyun 203*4882a593Smuzhiyun parser.add_option("-I", "--ignore-deps", action="append", 204*4882a593Smuzhiyun dest="extra_assume_provided", default=[], 205*4882a593Smuzhiyun help="Assume these dependencies don't exist and are already provided " 206*4882a593Smuzhiyun "(equivalent to ASSUME_PROVIDED). Useful to make dependency " 207*4882a593Smuzhiyun "graphs more appealing") 208*4882a593Smuzhiyun 209*4882a593Smuzhiyun parser.add_option("-l", "--log-domains", action="append", dest="debug_domains", default=[], 210*4882a593Smuzhiyun help="Show debug logging for the specified logging domains") 211*4882a593Smuzhiyun 212*4882a593Smuzhiyun parser.add_option("-P", "--profile", action="store_true", dest="profile", default=False, 213*4882a593Smuzhiyun help="Profile the command and save reports.") 214*4882a593Smuzhiyun 215*4882a593Smuzhiyun # @CHOICES@ is substituted out by BitbakeHelpFormatter above 216*4882a593Smuzhiyun parser.add_option("-u", "--ui", action="store", dest="ui", 217*4882a593Smuzhiyun default=os.environ.get('BITBAKE_UI', 'knotty'), 218*4882a593Smuzhiyun help="The user interface to use (@CHOICES@ - default %default).") 219*4882a593Smuzhiyun 220*4882a593Smuzhiyun parser.add_option("", "--token", action="store", dest="xmlrpctoken", 221*4882a593Smuzhiyun default=os.environ.get("BBTOKEN"), 222*4882a593Smuzhiyun help="Specify the connection token to be used when connecting " 223*4882a593Smuzhiyun "to a remote server.") 224*4882a593Smuzhiyun 225*4882a593Smuzhiyun parser.add_option("", "--revisions-changed", action="store_true", 226*4882a593Smuzhiyun dest="revisions_changed", default=False, 227*4882a593Smuzhiyun help="Set the exit code depending on whether upstream floating " 228*4882a593Smuzhiyun "revisions have changed or not.") 229*4882a593Smuzhiyun 230*4882a593Smuzhiyun parser.add_option("", "--server-only", action="store_true", 231*4882a593Smuzhiyun dest="server_only", default=False, 232*4882a593Smuzhiyun help="Run bitbake without a UI, only starting a server " 233*4882a593Smuzhiyun "(cooker) process.") 234*4882a593Smuzhiyun 235*4882a593Smuzhiyun parser.add_option("-B", "--bind", action="store", dest="bind", default=False, 236*4882a593Smuzhiyun help="The name/address for the bitbake xmlrpc server to bind to.") 237*4882a593Smuzhiyun 238*4882a593Smuzhiyun parser.add_option("-T", "--idle-timeout", type=float, dest="server_timeout", 239*4882a593Smuzhiyun default=os.getenv("BB_SERVER_TIMEOUT"), 240*4882a593Smuzhiyun help="Set timeout to unload bitbake server due to inactivity, " 241*4882a593Smuzhiyun "set to -1 means no unload, " 242*4882a593Smuzhiyun "default: Environment variable BB_SERVER_TIMEOUT.") 243*4882a593Smuzhiyun 244*4882a593Smuzhiyun parser.add_option("", "--no-setscene", action="store_true", 245*4882a593Smuzhiyun dest="nosetscene", default=False, 246*4882a593Smuzhiyun help="Do not run any setscene tasks. sstate will be ignored and " 247*4882a593Smuzhiyun "everything needed, built.") 248*4882a593Smuzhiyun 249*4882a593Smuzhiyun parser.add_option("", "--skip-setscene", action="store_true", 250*4882a593Smuzhiyun dest="skipsetscene", default=False, 251*4882a593Smuzhiyun help="Skip setscene tasks if they would be executed. Tasks previously " 252*4882a593Smuzhiyun "restored from sstate will be kept, unlike --no-setscene") 253*4882a593Smuzhiyun 254*4882a593Smuzhiyun parser.add_option("", "--setscene-only", action="store_true", 255*4882a593Smuzhiyun dest="setsceneonly", default=False, 256*4882a593Smuzhiyun help="Only run setscene tasks, don't run any real tasks.") 257*4882a593Smuzhiyun 258*4882a593Smuzhiyun parser.add_option("", "--remote-server", action="store", dest="remote_server", 259*4882a593Smuzhiyun default=os.environ.get("BBSERVER"), 260*4882a593Smuzhiyun help="Connect to the specified server.") 261*4882a593Smuzhiyun 262*4882a593Smuzhiyun parser.add_option("-m", "--kill-server", action="store_true", 263*4882a593Smuzhiyun dest="kill_server", default=False, 264*4882a593Smuzhiyun help="Terminate any running bitbake server.") 265*4882a593Smuzhiyun 266*4882a593Smuzhiyun parser.add_option("", "--observe-only", action="store_true", 267*4882a593Smuzhiyun dest="observe_only", default=False, 268*4882a593Smuzhiyun help="Connect to a server as an observing-only client.") 269*4882a593Smuzhiyun 270*4882a593Smuzhiyun parser.add_option("", "--status-only", action="store_true", 271*4882a593Smuzhiyun dest="status_only", default=False, 272*4882a593Smuzhiyun help="Check the status of the remote bitbake server.") 273*4882a593Smuzhiyun 274*4882a593Smuzhiyun parser.add_option("-w", "--write-log", action="store", dest="writeeventlog", 275*4882a593Smuzhiyun default=os.environ.get("BBEVENTLOG"), 276*4882a593Smuzhiyun help="Writes the event log of the build to a bitbake event json file. " 277*4882a593Smuzhiyun "Use '' (empty string) to assign the name automatically.") 278*4882a593Smuzhiyun 279*4882a593Smuzhiyun parser.add_option("", "--runall", action="append", dest="runall", 280*4882a593Smuzhiyun help="Run the specified task for any recipe in the taskgraph of the specified target (even if it wouldn't otherwise have run).") 281*4882a593Smuzhiyun 282*4882a593Smuzhiyun parser.add_option("", "--runonly", action="append", dest="runonly", 283*4882a593Smuzhiyun help="Run only the specified task within the taskgraph of the specified targets (and any task dependencies those tasks may have).") 284*4882a593Smuzhiyun return parser 285*4882a593Smuzhiyun 286*4882a593Smuzhiyun 287*4882a593Smuzhiyunclass BitBakeConfigParameters(cookerdata.ConfigParameters): 288*4882a593Smuzhiyun def parseCommandLine(self, argv=sys.argv): 289*4882a593Smuzhiyun parser = create_bitbake_parser() 290*4882a593Smuzhiyun options, targets = parser.parse_args(argv) 291*4882a593Smuzhiyun 292*4882a593Smuzhiyun if options.quiet and options.verbose: 293*4882a593Smuzhiyun parser.error("options --quiet and --verbose are mutually exclusive") 294*4882a593Smuzhiyun 295*4882a593Smuzhiyun if options.quiet and options.debug: 296*4882a593Smuzhiyun parser.error("options --quiet and --debug are mutually exclusive") 297*4882a593Smuzhiyun 298*4882a593Smuzhiyun # use configuration files from environment variables 299*4882a593Smuzhiyun if "BBPRECONF" in os.environ: 300*4882a593Smuzhiyun options.prefile.append(os.environ["BBPRECONF"]) 301*4882a593Smuzhiyun 302*4882a593Smuzhiyun if "BBPOSTCONF" in os.environ: 303*4882a593Smuzhiyun options.postfile.append(os.environ["BBPOSTCONF"]) 304*4882a593Smuzhiyun 305*4882a593Smuzhiyun # fill in proper log name if not supplied 306*4882a593Smuzhiyun if options.writeeventlog is not None and len(options.writeeventlog) == 0: 307*4882a593Smuzhiyun from datetime import datetime 308*4882a593Smuzhiyun eventlog = "bitbake_eventlog_%s.json" % datetime.now().strftime("%Y%m%d%H%M%S") 309*4882a593Smuzhiyun options.writeeventlog = eventlog 310*4882a593Smuzhiyun 311*4882a593Smuzhiyun if options.bind: 312*4882a593Smuzhiyun try: 313*4882a593Smuzhiyun #Checking that the port is a number and is a ':' delimited value 314*4882a593Smuzhiyun (host, port) = options.bind.split(':') 315*4882a593Smuzhiyun port = int(port) 316*4882a593Smuzhiyun except (ValueError,IndexError): 317*4882a593Smuzhiyun raise BBMainException("FATAL: Malformed host:port bind parameter") 318*4882a593Smuzhiyun options.xmlrpcinterface = (host, port) 319*4882a593Smuzhiyun else: 320*4882a593Smuzhiyun options.xmlrpcinterface = (None, 0) 321*4882a593Smuzhiyun 322*4882a593Smuzhiyun return options, targets[1:] 323*4882a593Smuzhiyun 324*4882a593Smuzhiyun 325*4882a593Smuzhiyundef bitbake_main(configParams, configuration): 326*4882a593Smuzhiyun 327*4882a593Smuzhiyun # Python multiprocessing requires /dev/shm on Linux 328*4882a593Smuzhiyun if sys.platform.startswith('linux') and not os.access('/dev/shm', os.W_OK | os.X_OK): 329*4882a593Smuzhiyun raise BBMainException("FATAL: /dev/shm does not exist or is not writable") 330*4882a593Smuzhiyun 331*4882a593Smuzhiyun # Unbuffer stdout to avoid log truncation in the event 332*4882a593Smuzhiyun # of an unorderly exit as well as to provide timely 333*4882a593Smuzhiyun # updates to log files for use with tail 334*4882a593Smuzhiyun try: 335*4882a593Smuzhiyun if sys.stdout.name == '<stdout>': 336*4882a593Smuzhiyun # Reopen with O_SYNC (unbuffered) 337*4882a593Smuzhiyun fl = fcntl.fcntl(sys.stdout.fileno(), fcntl.F_GETFL) 338*4882a593Smuzhiyun fl |= os.O_SYNC 339*4882a593Smuzhiyun fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, fl) 340*4882a593Smuzhiyun except: 341*4882a593Smuzhiyun pass 342*4882a593Smuzhiyun 343*4882a593Smuzhiyun if configParams.server_only and configParams.remote_server: 344*4882a593Smuzhiyun raise BBMainException("FATAL: The '--server-only' option conflicts with %s.\n" % 345*4882a593Smuzhiyun ("the BBSERVER environment variable" if "BBSERVER" in os.environ \ 346*4882a593Smuzhiyun else "the '--remote-server' option")) 347*4882a593Smuzhiyun 348*4882a593Smuzhiyun if configParams.observe_only and not (configParams.remote_server or configParams.bind): 349*4882a593Smuzhiyun raise BBMainException("FATAL: '--observe-only' can only be used by UI clients " 350*4882a593Smuzhiyun "connecting to a server.\n") 351*4882a593Smuzhiyun 352*4882a593Smuzhiyun if "BBDEBUG" in os.environ: 353*4882a593Smuzhiyun level = int(os.environ["BBDEBUG"]) 354*4882a593Smuzhiyun if level > configParams.debug: 355*4882a593Smuzhiyun configParams.debug = level 356*4882a593Smuzhiyun 357*4882a593Smuzhiyun bb.msg.init_msgconfig(configParams.verbose, configParams.debug, 358*4882a593Smuzhiyun configParams.debug_domains) 359*4882a593Smuzhiyun 360*4882a593Smuzhiyun server_connection, ui_module = setup_bitbake(configParams) 361*4882a593Smuzhiyun # No server connection 362*4882a593Smuzhiyun if server_connection is None: 363*4882a593Smuzhiyun if configParams.status_only: 364*4882a593Smuzhiyun return 1 365*4882a593Smuzhiyun if configParams.kill_server: 366*4882a593Smuzhiyun return 0 367*4882a593Smuzhiyun 368*4882a593Smuzhiyun if not configParams.server_only: 369*4882a593Smuzhiyun if configParams.status_only: 370*4882a593Smuzhiyun server_connection.terminate() 371*4882a593Smuzhiyun return 0 372*4882a593Smuzhiyun 373*4882a593Smuzhiyun try: 374*4882a593Smuzhiyun for event in bb.event.ui_queue: 375*4882a593Smuzhiyun server_connection.events.queue_event(event) 376*4882a593Smuzhiyun bb.event.ui_queue = [] 377*4882a593Smuzhiyun 378*4882a593Smuzhiyun return ui_module.main(server_connection.connection, server_connection.events, 379*4882a593Smuzhiyun configParams) 380*4882a593Smuzhiyun finally: 381*4882a593Smuzhiyun server_connection.terminate() 382*4882a593Smuzhiyun else: 383*4882a593Smuzhiyun return 0 384*4882a593Smuzhiyun 385*4882a593Smuzhiyun return 1 386*4882a593Smuzhiyun 387*4882a593Smuzhiyundef setup_bitbake(configParams, extrafeatures=None): 388*4882a593Smuzhiyun # Ensure logging messages get sent to the UI as events 389*4882a593Smuzhiyun handler = bb.event.LogHandler() 390*4882a593Smuzhiyun if not configParams.status_only: 391*4882a593Smuzhiyun # In status only mode there are no logs and no UI 392*4882a593Smuzhiyun logger.addHandler(handler) 393*4882a593Smuzhiyun 394*4882a593Smuzhiyun if configParams.server_only: 395*4882a593Smuzhiyun featureset = [] 396*4882a593Smuzhiyun ui_module = None 397*4882a593Smuzhiyun else: 398*4882a593Smuzhiyun ui_module = import_extension_module(bb.ui, configParams.ui, 'main') 399*4882a593Smuzhiyun # Collect the feature set for the UI 400*4882a593Smuzhiyun featureset = getattr(ui_module, "featureSet", []) 401*4882a593Smuzhiyun 402*4882a593Smuzhiyun if extrafeatures: 403*4882a593Smuzhiyun for feature in extrafeatures: 404*4882a593Smuzhiyun if not feature in featureset: 405*4882a593Smuzhiyun featureset.append(feature) 406*4882a593Smuzhiyun 407*4882a593Smuzhiyun server_connection = None 408*4882a593Smuzhiyun 409*4882a593Smuzhiyun # Clear away any spurious environment variables while we stoke up the cooker 410*4882a593Smuzhiyun # (done after import_extension_module() above since for example import gi triggers env var usage) 411*4882a593Smuzhiyun cleanedvars = bb.utils.clean_environment() 412*4882a593Smuzhiyun 413*4882a593Smuzhiyun if configParams.remote_server: 414*4882a593Smuzhiyun # Connect to a remote XMLRPC server 415*4882a593Smuzhiyun server_connection = bb.server.xmlrpcclient.connectXMLRPC(configParams.remote_server, featureset, 416*4882a593Smuzhiyun configParams.observe_only, configParams.xmlrpctoken) 417*4882a593Smuzhiyun else: 418*4882a593Smuzhiyun retries = 8 419*4882a593Smuzhiyun while retries: 420*4882a593Smuzhiyun try: 421*4882a593Smuzhiyun topdir, lock = lockBitbake() 422*4882a593Smuzhiyun sockname = topdir + "/bitbake.sock" 423*4882a593Smuzhiyun if lock: 424*4882a593Smuzhiyun if configParams.status_only or configParams.kill_server: 425*4882a593Smuzhiyun logger.info("bitbake server is not running.") 426*4882a593Smuzhiyun lock.close() 427*4882a593Smuzhiyun return None, None 428*4882a593Smuzhiyun # we start a server with a given featureset 429*4882a593Smuzhiyun logger.info("Starting bitbake server...") 430*4882a593Smuzhiyun # Clear the event queue since we already displayed messages 431*4882a593Smuzhiyun bb.event.ui_queue = [] 432*4882a593Smuzhiyun server = bb.server.process.BitBakeServer(lock, sockname, featureset, configParams.server_timeout, configParams.xmlrpcinterface) 433*4882a593Smuzhiyun 434*4882a593Smuzhiyun else: 435*4882a593Smuzhiyun logger.info("Reconnecting to bitbake server...") 436*4882a593Smuzhiyun if not os.path.exists(sockname): 437*4882a593Smuzhiyun logger.info("Previous bitbake instance shutting down?, waiting to retry...") 438*4882a593Smuzhiyun i = 0 439*4882a593Smuzhiyun lock = None 440*4882a593Smuzhiyun # Wait for 5s or until we can get the lock 441*4882a593Smuzhiyun while not lock and i < 50: 442*4882a593Smuzhiyun time.sleep(0.1) 443*4882a593Smuzhiyun _, lock = lockBitbake() 444*4882a593Smuzhiyun i += 1 445*4882a593Smuzhiyun if lock: 446*4882a593Smuzhiyun bb.utils.unlockfile(lock) 447*4882a593Smuzhiyun raise bb.server.process.ProcessTimeout("Bitbake still shutting down as socket exists but no lock?") 448*4882a593Smuzhiyun if not configParams.server_only: 449*4882a593Smuzhiyun server_connection = bb.server.process.connectProcessServer(sockname, featureset) 450*4882a593Smuzhiyun 451*4882a593Smuzhiyun if server_connection or configParams.server_only: 452*4882a593Smuzhiyun break 453*4882a593Smuzhiyun except BBMainFatal: 454*4882a593Smuzhiyun raise 455*4882a593Smuzhiyun except (Exception, bb.server.process.ProcessTimeout, SystemExit) as e: 456*4882a593Smuzhiyun # SystemExit does not inherit from the Exception class, needs to be included explicitly 457*4882a593Smuzhiyun if not retries: 458*4882a593Smuzhiyun raise 459*4882a593Smuzhiyun retries -= 1 460*4882a593Smuzhiyun tryno = 8 - retries 461*4882a593Smuzhiyun if isinstance(e, (bb.server.process.ProcessTimeout, BrokenPipeError, EOFError, SystemExit)): 462*4882a593Smuzhiyun logger.info("Retrying server connection (#%d)..." % tryno) 463*4882a593Smuzhiyun else: 464*4882a593Smuzhiyun logger.info("Retrying server connection (#%d)... (%s)" % (tryno, traceback.format_exc())) 465*4882a593Smuzhiyun 466*4882a593Smuzhiyun if not retries: 467*4882a593Smuzhiyun bb.fatal("Unable to connect to bitbake server, or start one (server startup failures would be in bitbake-cookerdaemon.log).") 468*4882a593Smuzhiyun bb.event.print_ui_queue() 469*4882a593Smuzhiyun if retries < 5: 470*4882a593Smuzhiyun time.sleep(5) 471*4882a593Smuzhiyun 472*4882a593Smuzhiyun if configParams.kill_server: 473*4882a593Smuzhiyun server_connection.connection.terminateServer() 474*4882a593Smuzhiyun server_connection.terminate() 475*4882a593Smuzhiyun bb.event.ui_queue = [] 476*4882a593Smuzhiyun logger.info("Terminated bitbake server.") 477*4882a593Smuzhiyun return None, None 478*4882a593Smuzhiyun 479*4882a593Smuzhiyun # Restore the environment in case the UI needs it 480*4882a593Smuzhiyun for k in cleanedvars: 481*4882a593Smuzhiyun os.environ[k] = cleanedvars[k] 482*4882a593Smuzhiyun 483*4882a593Smuzhiyun logger.removeHandler(handler) 484*4882a593Smuzhiyun 485*4882a593Smuzhiyun return server_connection, ui_module 486*4882a593Smuzhiyun 487*4882a593Smuzhiyundef lockBitbake(): 488*4882a593Smuzhiyun topdir = bb.cookerdata.findTopdir() 489*4882a593Smuzhiyun if not topdir: 490*4882a593Smuzhiyun bb.error("Unable to find conf/bblayers.conf or conf/bitbake.conf. BBPATH is unset and/or not in a build directory?") 491*4882a593Smuzhiyun raise BBMainFatal 492*4882a593Smuzhiyun lockfile = topdir + "/bitbake.lock" 493*4882a593Smuzhiyun return topdir, bb.utils.lockfile(lockfile, False, False) 494*4882a593Smuzhiyun 495