xref: /openbmc/openbmc/poky/bitbake/lib/bb/cookerdata.py (revision f1e5d6968976c2341c6d554bfcc8895f1b33c26b)
1
2#
3# Copyright (C) 2003, 2004  Chris Larson
4# Copyright (C) 2003, 2004  Phil Blundell
5# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
6# Copyright (C) 2005        Holger Hans Peter Freyther
7# Copyright (C) 2005        ROAD GmbH
8# Copyright (C) 2006        Richard Purdie
9#
10# SPDX-License-Identifier: GPL-2.0-only
11#
12
13import logging
14import os
15import re
16import sys
17import hashlib
18from functools import wraps
19import bb
20from bb import data
21import bb.parse
22
23logger      = logging.getLogger("BitBake")
24parselog    = logging.getLogger("BitBake.Parsing")
25
26class ConfigParameters(object):
27    def __init__(self, argv=None):
28        self.options, targets = self.parseCommandLine(argv or sys.argv)
29        self.environment = self.parseEnvironment()
30
31        self.options.pkgs_to_build = targets or []
32
33        for key, val in self.options.__dict__.items():
34            setattr(self, key, val)
35
36    def parseCommandLine(self, argv=sys.argv):
37        raise Exception("Caller must implement commandline option parsing")
38
39    def parseEnvironment(self):
40        return os.environ.copy()
41
42    def updateFromServer(self, server):
43        if not self.options.cmd:
44            defaulttask, error = server.runCommand(["getVariable", "BB_DEFAULT_TASK"])
45            if error:
46                raise Exception("Unable to get the value of BB_DEFAULT_TASK from the server: %s" % error)
47            self.options.cmd = defaulttask or "build"
48        _, error = server.runCommand(["setConfig", "cmd", self.options.cmd])
49        if error:
50            raise Exception("Unable to set configuration option 'cmd' on the server: %s" % error)
51
52        if not self.options.pkgs_to_build:
53            bbpkgs, error = server.runCommand(["getVariable", "BBTARGETS"])
54            if error:
55                raise Exception("Unable to get the value of BBTARGETS from the server: %s" % error)
56            if bbpkgs:
57                self.options.pkgs_to_build.extend(bbpkgs.split())
58
59    def updateToServer(self, server, environment):
60        options = {}
61        for o in ["halt", "force", "invalidate_stamp",
62                  "dry_run", "dump_signatures",
63                  "extra_assume_provided", "profile",
64                  "prefile", "postfile", "server_timeout",
65                  "nosetscene", "setsceneonly", "skipsetscene",
66                  "runall", "runonly", "writeeventlog"]:
67            options[o] = getattr(self.options, o)
68
69        options['build_verbose_shell'] = self.options.verbose
70        options['build_verbose_stdout'] = self.options.verbose
71        options['default_loglevel'] = bb.msg.loggerDefaultLogLevel
72        options['debug_domains'] = bb.msg.loggerDefaultDomains
73
74        ret, error = server.runCommand(["updateConfig", options, environment, sys.argv])
75        if error:
76            raise Exception("Unable to update the server configuration with local parameters: %s" % error)
77
78    def parseActions(self):
79        # Parse any commandline into actions
80        action = {'action':None, 'msg':None}
81        if self.options.show_environment:
82            if 'world' in self.options.pkgs_to_build:
83                action['msg'] = "'world' is not a valid target for --environment."
84            elif 'universe' in self.options.pkgs_to_build:
85                action['msg'] = "'universe' is not a valid target for --environment."
86            elif len(self.options.pkgs_to_build) > 1:
87                action['msg'] = "Only one target can be used with the --environment option."
88            elif self.options.buildfile and len(self.options.pkgs_to_build) > 0:
89                action['msg'] = "No target should be used with the --environment and --buildfile options."
90            elif self.options.pkgs_to_build:
91                action['action'] = ["showEnvironmentTarget", self.options.pkgs_to_build]
92            else:
93                action['action'] = ["showEnvironment", self.options.buildfile]
94        elif self.options.buildfile is not None:
95            action['action'] = ["buildFile", self.options.buildfile, self.options.cmd]
96        elif self.options.revisions_changed:
97            action['action'] = ["compareRevisions"]
98        elif self.options.show_versions:
99            action['action'] = ["showVersions"]
100        elif self.options.parse_only:
101            action['action'] = ["parseFiles"]
102        elif self.options.dot_graph:
103            if self.options.pkgs_to_build:
104                action['action'] = ["generateDotGraph", self.options.pkgs_to_build, self.options.cmd]
105            else:
106                action['msg'] = "Please specify a package name for dependency graph generation."
107        else:
108            if self.options.pkgs_to_build:
109                action['action'] = ["buildTargets", self.options.pkgs_to_build, self.options.cmd]
110            else:
111                #action['msg'] = "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information."
112                action = None
113        self.options.initialaction = action
114        return action
115
116class CookerConfiguration(object):
117    """
118    Manages build options and configurations for one run
119    """
120
121    def __init__(self):
122        self.debug_domains = bb.msg.loggerDefaultDomains
123        self.default_loglevel = bb.msg.loggerDefaultLogLevel
124        self.extra_assume_provided = []
125        self.prefile = []
126        self.postfile = []
127        self.cmd = None
128        self.halt = True
129        self.force = False
130        self.profile = False
131        self.nosetscene = False
132        self.setsceneonly = False
133        self.skipsetscene = False
134        self.invalidate_stamp = False
135        self.dump_signatures = []
136        self.build_verbose_shell = False
137        self.build_verbose_stdout = False
138        self.dry_run = False
139        self.tracking = False
140        self.writeeventlog = False
141        self.limited_deps = False
142        self.runall = []
143        self.runonly = []
144
145        self.env = {}
146
147    def __getstate__(self):
148        state = {}
149        for key in self.__dict__.keys():
150            state[key] = getattr(self, key)
151        return state
152
153    def __setstate__(self,state):
154        for k in state:
155            setattr(self, k, state[k])
156
157
158def catch_parse_error(func):
159    """Exception handling bits for our parsing"""
160    @wraps(func)
161    def wrapped(fn, *args):
162        try:
163            return func(fn, *args)
164        except Exception as exc:
165            import traceback
166
167            bbdir = os.path.dirname(__file__) + os.sep
168            exc_class, exc, tb = sys.exc_info()
169            for tb in iter(lambda: tb.tb_next, None):
170                # Skip frames in bitbake itself, we only want the metadata
171                fn, _, _, _ = traceback.extract_tb(tb, 1)[0]
172                if not fn.startswith(bbdir):
173                    break
174            parselog.critical("Unable to parse %s" % fn, exc_info=(exc_class, exc, tb))
175            raise bb.BBHandledException()
176    return wrapped
177
178@catch_parse_error
179def parse_config_file(fn, data, include=True):
180    return bb.parse.handle(fn, data, include, baseconfig=True)
181
182@catch_parse_error
183def _inherit(bbclass, data):
184    bb.parse.BBHandler.inherit(bbclass, "configuration INHERITs", 0, data)
185    return data
186
187def findConfigFile(configfile, data):
188    search = []
189    bbpath = data.getVar("BBPATH")
190    if bbpath:
191        for i in bbpath.split(":"):
192            search.append(os.path.join(i, "conf", configfile))
193    path = os.getcwd()
194    while path != "/":
195        search.append(os.path.join(path, "conf", configfile))
196        path, _ = os.path.split(path)
197
198    for i in search:
199        if os.path.exists(i):
200            return i
201
202    return None
203
204#
205# We search for a conf/bblayers.conf under an entry in BBPATH or in cwd working
206# up to /. If that fails, bitbake would fall back to cwd.
207#
208
209def findTopdir():
210    d = bb.data.init()
211    bbpath = None
212    if 'BBPATH' in os.environ:
213        bbpath = os.environ['BBPATH']
214        d.setVar('BBPATH', bbpath)
215
216    layerconf = findConfigFile("bblayers.conf", d)
217    if layerconf:
218        return os.path.dirname(os.path.dirname(layerconf))
219
220    return os.path.abspath(os.getcwd())
221
222class CookerDataBuilder(object):
223
224    def __init__(self, cookercfg, worker = False):
225
226        self.prefiles = cookercfg.prefile
227        self.postfiles = cookercfg.postfile
228        self.tracking = cookercfg.tracking
229
230        bb.utils.set_context(bb.utils.clean_context())
231        bb.event.set_class_handlers(bb.event.clean_class_handlers())
232        self.basedata = bb.data.init()
233        if self.tracking:
234            self.basedata.enableTracking()
235
236        # Keep a datastore of the initial environment variables and their
237        # values from when BitBake was launched to enable child processes
238        # to use environment variables which have been cleaned from the
239        # BitBake processes env
240        self.savedenv = bb.data.init()
241        for k in cookercfg.env:
242            self.savedenv.setVar(k, cookercfg.env[k])
243            if k in bb.data_smart.bitbake_renamed_vars:
244                bb.error('Shell environment variable %s has been renamed to %s' % (k, bb.data_smart.bitbake_renamed_vars[k]))
245                bb.fatal("Exiting to allow enviroment variables to be corrected")
246
247        filtered_keys = bb.utils.approved_variables()
248        bb.data.inheritFromOS(self.basedata, self.savedenv, filtered_keys)
249        self.basedata.setVar("BB_ORIGENV", self.savedenv)
250        self.basedata.setVar("__bbclasstype", "global")
251
252        if worker:
253            self.basedata.setVar("BB_WORKERCONTEXT", "1")
254
255        self.data = self.basedata
256        self.mcdata = {}
257
258    def calc_datastore_hashes(self):
259        data_hash = hashlib.sha256()
260        data_hash.update(self.data.get_hash().encode('utf-8'))
261        multiconfig = (self.data.getVar("BBMULTICONFIG") or "").split()
262        for config in multiconfig:
263            data_hash.update(self.mcdata[config].get_hash().encode('utf-8'))
264        self.data_hash = data_hash.hexdigest()
265
266    def parseBaseConfiguration(self, worker=False):
267        mcdata = {}
268        try:
269            self.data = self.parseConfigurationFiles(self.prefiles, self.postfiles)
270
271            servercontext = self.data.getVar("BB_WORKERCONTEXT", False) is None and not worker
272            bb.fetch.fetcher_init(self.data, servercontext)
273            bb.parse.init_parser(self.data)
274
275            bb.event.fire(bb.event.ConfigParsed(), self.data)
276
277            reparse_cnt = 0
278            while self.data.getVar("BB_INVALIDCONF", False) is True:
279                if reparse_cnt > 20:
280                    logger.error("Configuration has been re-parsed over 20 times, "
281                                 "breaking out of the loop...")
282                    raise Exception("Too deep config re-parse loop. Check locations where "
283                                    "BB_INVALIDCONF is being set (ConfigParsed event handlers)")
284                self.data.setVar("BB_INVALIDCONF", False)
285                self.data = self.parseConfigurationFiles(self.prefiles, self.postfiles)
286                reparse_cnt += 1
287                bb.event.fire(bb.event.ConfigParsed(), self.data)
288
289            bb.parse.init_parser(self.data)
290            mcdata[''] = self.data
291
292            multiconfig = (self.data.getVar("BBMULTICONFIG") or "").split()
293            for config in multiconfig:
294                if config[0].isdigit():
295                    bb.fatal("Multiconfig name '%s' is invalid as multiconfigs cannot start with a digit" % config)
296                parsed_mcdata = self.parseConfigurationFiles(self.prefiles, self.postfiles, config)
297                bb.event.fire(bb.event.ConfigParsed(), parsed_mcdata)
298                mcdata[config] = parsed_mcdata
299            if multiconfig:
300                bb.event.fire(bb.event.MultiConfigParsed(mcdata), self.data)
301
302        except bb.data_smart.ExpansionError as e:
303            logger.error(str(e))
304            raise bb.BBHandledException()
305
306        bb.codeparser.update_module_dependencies(self.data)
307
308        # Handle obsolete variable names
309        d = self.data
310        renamedvars = d.getVarFlags('BB_RENAMED_VARIABLES') or {}
311        renamedvars.update(bb.data_smart.bitbake_renamed_vars)
312        issues = False
313        for v in renamedvars:
314            if d.getVar(v) != None or d.hasOverrides(v):
315                issues = True
316                loginfo = {}
317                history = d.varhistory.get_variable_refs(v)
318                for h in history:
319                    for line in history[h]:
320                        loginfo = {'file' : h, 'line' : line}
321                        bb.data.data_smart._print_rename_error(v, loginfo, renamedvars)
322                if not history:
323                    bb.data.data_smart._print_rename_error(v, loginfo, renamedvars)
324        if issues:
325            raise bb.BBHandledException()
326
327        for mc in mcdata:
328            mcdata[mc].renameVar("__depends", "__base_depends")
329            mcdata[mc].setVar("__bbclasstype", "recipe")
330
331        # Create a copy so we can reset at a later date when UIs disconnect
332        self.mcorigdata = mcdata
333        for mc in mcdata:
334            self.mcdata[mc] = bb.data.createCopy(mcdata[mc])
335        self.data = self.mcdata['']
336        self.calc_datastore_hashes()
337
338    def reset(self):
339        # We may not have run parseBaseConfiguration() yet
340        if not hasattr(self, 'mcorigdata'):
341            return
342        for mc in self.mcorigdata:
343            self.mcdata[mc] = bb.data.createCopy(self.mcorigdata[mc])
344        self.data = self.mcdata['']
345
346    def _findLayerConf(self, data):
347        return findConfigFile("bblayers.conf", data)
348
349    def parseConfigurationFiles(self, prefiles, postfiles, mc = "default"):
350        data = bb.data.createCopy(self.basedata)
351        data.setVar("BB_CURRENT_MC", mc)
352
353        # Parse files for loading *before* bitbake.conf and any includes
354        for f in prefiles:
355            data = parse_config_file(f, data)
356
357        layerconf = self._findLayerConf(data)
358        if layerconf:
359            parselog.debug2("Found bblayers.conf (%s)", layerconf)
360            # By definition bblayers.conf is in conf/ of TOPDIR.
361            # We may have been called with cwd somewhere else so reset TOPDIR
362            data.setVar("TOPDIR", os.path.dirname(os.path.dirname(layerconf)))
363            data = parse_config_file(layerconf, data)
364
365            if not data.getVar("BB_CACHEDIR"):
366                data.setVar("BB_CACHEDIR", "${TOPDIR}/cache")
367
368            bb.codeparser.parser_cache_init(data.getVar("BB_CACHEDIR"))
369
370            layers = (data.getVar('BBLAYERS') or "").split()
371            broken_layers = []
372
373            if not layers:
374                bb.fatal("The bblayers.conf file doesn't contain any BBLAYERS definition")
375
376            data = bb.data.createCopy(data)
377            approved = bb.utils.approved_variables()
378
379            # Check whether present layer directories exist
380            for layer in layers:
381                if not os.path.isdir(layer):
382                    broken_layers.append(layer)
383
384            if broken_layers:
385                parselog.critical("The following layer directories do not exist:")
386                for layer in broken_layers:
387                    parselog.critical("   %s", layer)
388                parselog.critical("Please check BBLAYERS in %s" % (layerconf))
389                raise bb.BBHandledException()
390
391            layerseries = None
392            compat_entries = {}
393            for layer in layers:
394                parselog.debug2("Adding layer %s", layer)
395                if 'HOME' in approved and '~' in layer:
396                    layer = os.path.expanduser(layer)
397                if layer.endswith('/'):
398                    layer = layer.rstrip('/')
399                data.setVar('LAYERDIR', layer)
400                data.setVar('LAYERDIR_RE', re.escape(layer))
401                data = parse_config_file(os.path.join(layer, "conf", "layer.conf"), data)
402                data.expandVarref('LAYERDIR')
403                data.expandVarref('LAYERDIR_RE')
404
405                # Sadly we can't have nice things.
406                # Some layers think they're going to be 'clever' and copy the values from
407                # another layer, e.g. using ${LAYERSERIES_COMPAT_core}. The whole point of
408                # this mechanism is to make it clear which releases a layer supports and
409                # show when a layer master branch is bitrotting and is unmaintained.
410                # We therefore avoid people doing this here.
411                collections = (data.getVar('BBFILE_COLLECTIONS') or "").split()
412                for c in collections:
413                    compat_entry = data.getVar("LAYERSERIES_COMPAT_%s" % c)
414                    if compat_entry:
415                        compat_entries[c] = set(compat_entry.split())
416                        data.delVar("LAYERSERIES_COMPAT_%s" % c)
417                if not layerseries:
418                    layerseries = set((data.getVar("LAYERSERIES_CORENAMES") or "").split())
419                    if layerseries:
420                        data.delVar("LAYERSERIES_CORENAMES")
421
422            data.delVar('LAYERDIR_RE')
423            data.delVar('LAYERDIR')
424            for c in compat_entries:
425                data.setVar("LAYERSERIES_COMPAT_%s" % c, " ".join(sorted(compat_entries[c])))
426
427            bbfiles_dynamic = (data.getVar('BBFILES_DYNAMIC') or "").split()
428            collections = (data.getVar('BBFILE_COLLECTIONS') or "").split()
429            invalid = []
430            for entry in bbfiles_dynamic:
431                parts = entry.split(":", 1)
432                if len(parts) != 2:
433                    invalid.append(entry)
434                    continue
435                l, f = parts
436                invert = l[0] == "!"
437                if invert:
438                    l = l[1:]
439                if (l in collections and not invert) or (l not in collections and invert):
440                    data.appendVar("BBFILES", " " + f)
441            if invalid:
442                bb.fatal("BBFILES_DYNAMIC entries must be of the form {!}<collection name>:<filename pattern>, not:\n    %s" % "\n    ".join(invalid))
443
444            collections_tmp = collections[:]
445            for c in collections:
446                collections_tmp.remove(c)
447                if c in collections_tmp:
448                    bb.fatal("Found duplicated BBFILE_COLLECTIONS '%s', check bblayers.conf or layer.conf to fix it." % c)
449
450                compat = set()
451                if c in compat_entries:
452                    compat = compat_entries[c]
453                if compat and not layerseries:
454                    bb.fatal("No core layer found to work with layer '%s'. Missing entry in bblayers.conf?" % c)
455                if compat and not (compat & layerseries):
456                    bb.fatal("Layer %s is not compatible with the core layer which only supports these series: %s (layer is compatible with %s)"
457                              % (c, " ".join(layerseries), " ".join(compat)))
458                elif not compat and not data.getVar("BB_WORKERCONTEXT"):
459                    bb.warn("Layer %s should set LAYERSERIES_COMPAT_%s in its conf/layer.conf file to list the core layer names it is compatible with." % (c, c))
460
461            data.setVar("LAYERSERIES_CORENAMES", " ".join(sorted(layerseries)))
462
463        if not data.getVar("BBPATH"):
464            msg = "The BBPATH variable is not set"
465            if not layerconf:
466                msg += (" and bitbake did not find a conf/bblayers.conf file in"
467                        " the expected location.\nMaybe you accidentally"
468                        " invoked bitbake from the wrong directory?")
469            bb.fatal(msg)
470
471        if not data.getVar("TOPDIR"):
472            data.setVar("TOPDIR", os.path.abspath(os.getcwd()))
473        if not data.getVar("BB_CACHEDIR"):
474            data.setVar("BB_CACHEDIR", "${TOPDIR}/cache")
475        bb.codeparser.parser_cache_init(data.getVar("BB_CACHEDIR"))
476
477        data = parse_config_file(os.path.join("conf", "bitbake.conf"), data)
478
479        # Parse files for loading *after* bitbake.conf and any includes
480        for p in postfiles:
481            data = parse_config_file(p, data)
482
483        # Handle any INHERITs and inherit the base class
484        bbclasses  = ["base"] + (data.getVar('INHERIT') or "").split()
485        for bbclass in bbclasses:
486            data = _inherit(bbclass, data)
487
488        # Normally we only register event handlers at the end of parsing .bb files
489        # We register any handlers we've found so far here...
490        for var in data.getVar('__BBHANDLERS', False) or []:
491            handlerfn = data.getVarFlag(var, "filename", False)
492            if not handlerfn:
493                parselog.critical("Undefined event handler function '%s'" % var)
494                raise bb.BBHandledException()
495            handlerln = int(data.getVarFlag(var, "lineno", False))
496            bb.event.register(var, data.getVar(var, False),  (data.getVarFlag(var, "eventmask") or "").split(), handlerfn, handlerln, data)
497
498        data.setVar('BBINCLUDED',bb.parse.get_file_depends(data))
499
500        return data
501
502    @staticmethod
503    def _parse_recipe(bb_data, bbfile, appends, mc, layername):
504        bb_data.setVar("__BBMULTICONFIG", mc)
505        bb_data.setVar("FILE_LAYERNAME", layername)
506
507        bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
508        bb.parse.cached_mtime_noerror(bbfile_loc)
509
510        if appends:
511            bb_data.setVar('__BBAPPEND', " ".join(appends))
512
513        return bb.parse.handle(bbfile, bb_data)
514
515    def parseRecipeVariants(self, bbfile, appends, virtonly=False, mc=None, layername=None):
516        """
517        Load and parse one .bb build file
518        Return the data and whether parsing resulted in the file being skipped
519        """
520
521        if virtonly:
522            (bbfile, virtual, mc) = bb.cache.virtualfn2realfn(bbfile)
523            bb_data = self.mcdata[mc].createCopy()
524            bb_data.setVar("__ONLYFINALISE", virtual or "default")
525            return self._parse_recipe(bb_data, bbfile, appends, mc, layername)
526
527        if mc is not None:
528            bb_data = self.mcdata[mc].createCopy()
529            return self._parse_recipe(bb_data, bbfile, appends, mc, layername)
530
531        bb_data = self.data.createCopy()
532        datastores = self._parse_recipe(bb_data, bbfile, appends, '', layername)
533
534        for mc in self.mcdata:
535            if not mc:
536                continue
537            bb_data = self.mcdata[mc].createCopy()
538            newstores = self._parse_recipe(bb_data, bbfile, appends, mc, layername)
539            for ns in newstores:
540                datastores["mc:%s:%s" % (mc, ns)] = newstores[ns]
541
542        return datastores
543
544    def parseRecipe(self, virtualfn, appends, layername):
545        """
546        Return a complete set of data for fn.
547        To do this, we need to parse the file.
548        """
549        logger.debug("Parsing %s (full)" % virtualfn)
550        (fn, virtual, mc) = bb.cache.virtualfn2realfn(virtualfn)
551        datastores = self.parseRecipeVariants(virtualfn, appends, virtonly=True, layername=layername)
552        return datastores[virtual]
553