blob: 261e36e9f83eb98149dfa668d1a0f5844671c5dd [file] [log] [blame]
# 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