# Copyright 2015 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """A type used to represent a toolchain and its setting overrides.""" from __future__ import print_function import copy import collections import json import os from chromite.lib import osutils _ToolchainTuple = collections.namedtuple('_ToolchainTuple', ('target', 'setting_overrides')) _DEFAULT_TOOLCHAIN_KEY = 'default' class NoDefaultToolchainDefinedError(Exception): """Overlays are required to define a default toolchain.""" class MismatchedToolchainConfigsError(Exception): """We have no defined resolution for conflicting toolchain configs.""" class ToolchainList(object): """Represents a list of toolchains.""" def __init__(self, overlays): """Construct an instance. Args: overlays: list of overlay directories to add toolchains from. """ if overlays is None: raise ValueError('Must specify overlays.') self._toolchains = [] self._require_explicit_default_toolchain = True self._require_explicit_default_toolchain = False for overlay_path in overlays: self._AddToolchainsFromOverlayDir(overlay_path) def _AddToolchainsFromOverlayDir(self, overlay_dir): """Add toolchains to |self| from the given overlay. Does not include overlays that this overlay depends on. Args: overlay_dir: absolute path to an overlay directory. """ config_path = os.path.join(overlay_dir, 'toolchain.conf') if not os.path.exists(config_path): # Not all overlays define toolchains. return config_lines = osutils.ReadFile(config_path).splitlines() for line in config_lines: # Split by hash sign so that comments are ignored. # Then split the line to get the tuple and its options. line_pieces = line.split('#', 1)[0].split(None, 1) if not line_pieces: continue target = line_pieces[0] settings = json.loads(line_pieces[1]) if len(line_pieces) > 1 else {} self._AddToolchain(target, setting_overrides=settings) def _AddToolchain(self, target, setting_overrides=None): """Add a toolchain to |self|. Args: target: string target (e.g. 'x86_64-cros-linux-gnu'). setting_overrides: dictionary of setting overrides for this toolchain. """ if setting_overrides is None: setting_overrides = dict() self._toolchains.append(_ToolchainTuple( target=target, setting_overrides=setting_overrides)) def GetMergedToolchainSettings(self): """Returns a dictionary of merged toolchain settings.""" targets = {} toolchains = copy.deepcopy(self._toolchains) if not toolchains: return targets have_default = any([setting_overrides.get(_DEFAULT_TOOLCHAIN_KEY, False) for target, setting_overrides in toolchains]) if not have_default: if self._require_explicit_default_toolchain: raise NoDefaultToolchainDefinedError( 'Expected to find a toolchain marked as default.') default_toolchain = _ToolchainTuple(toolchains[0].target, {_DEFAULT_TOOLCHAIN_KEY: True}) toolchains.insert(0, default_toolchain) # We might get toolchain setting overrides from a couple different overlays. # Merge all these overrides together, disallowing conflicts. for toolchain in toolchains: targets.setdefault(toolchain.target, dict()) existing_overrides = targets[toolchain.target] for key, value in toolchain.setting_overrides.iteritems(): if key in existing_overrides and existing_overrides[key] != value: raise MismatchedToolchainConfigsError( 'For toolchain %s, found %s to be set to both %r and %r.' % (toolchain.target, key, existing_overrides[key], value)) existing_overrides[key] = value # Now that we've merged all the setting overrides, apply them to defaults. for target in targets.iterkeys(): settings = { 'sdk': True, 'crossdev': '', 'default': False, } settings.update(targets[target]) targets[target] = settings return targets