xref: /OK3568_Linux_fs/yocto/poky/bitbake/lib/bb/main.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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