blob: 77d41867c819a6087ada52fcf7a0b117906bc55e [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.
"""Utilities to work with blueprints."""
from __future__ import print_function
import os
from chromite.lib import brick_lib
from chromite.lib import workspace_lib
# Field names for specifying initial configuration.
BRICKS_FIELD = 'bricks'
BSP_FIELD = 'bsp'
class BlueprintNotFoundError(Exception):
"""The blueprint does not exist."""
class BlueprintCreationError(Exception):
"""Blueprint creation failed."""
# TODO(dpursell): Enable this shorthand blueprint specifications for all CLI
# tools. http://brbug.com/931.
def ExpandBlueprintPath(path):
"""Expand a blueprint path using some common assumptions.
Makes the following changes to |path|:
1. Put non-paths in //blueprints (e.g. foo -> //blueprints/foo).
2. Add .json if no extension was given.
Args:
path: blueprint path.
Returns:
Modified blueprint path.
"""
# Non-path arguments should be put in //blueprints by default.
if '/' not in path:
path = os.path.join('//blueprints', path)
if os.path.splitext(path)[1] != '.json':
path += '.json'
return path
class Blueprint(object):
"""Encapsulates the interaction with a blueprint."""
def __init__(self, blueprint_loc, initial_config=None):
"""Instantiates a blueprint object.
Args:
blueprint_loc: blueprint locator. This can be a relative path to CWD, an
absolute path, or a relative path to the root of the workspace prefixed
with '//'.
initial_config: A dictionary of key-value pairs to seed a new blueprint
with if the specified blueprint doesn't already exist.
Raises:
BlueprintNotFoundError: No blueprint exists at |blueprint_loc| and no
|initial_config| was given to create a new one.
BlueprintCreationError: |initial_config| was specified but a file
already exists at |blueprint_loc|.
"""
self._path = (workspace_lib.LocatorToPath(blueprint_loc)
if workspace_lib.IsLocator(blueprint_loc) else blueprint_loc)
self._locator = workspace_lib.PathToLocator(self._path)
if initial_config is not None:
self._CreateBlueprintConfig(initial_config)
try:
self.config = workspace_lib.ReadConfigFile(self._path)
except IOError:
raise BlueprintNotFoundError('Blueprint %s not found.' % self._path)
def _CreateBlueprintConfig(self, config):
"""Create an initial blueprint config file.
Converts all brick paths in |config| into locators then saves the
configuration file to |self._path|.
Currently fails if |self._path| already exists, but could be
generalized to allow re-writing config files if needed.
Args:
config: configuration dictionary.
Raises:
BlueprintCreationError: A brick in |config| doesn't exist or an
error occurred while saving the config file.
"""
if os.path.exists(self._path):
raise BlueprintCreationError('File already exists at %s.' % self._path)
try:
# Turn brick specifications into locators.
if config.get(BRICKS_FIELD):
config[BRICKS_FIELD] = [brick_lib.Brick(b).brick_locator
for b in config[BRICKS_FIELD]]
if config.get(BSP_FIELD):
config[BSP_FIELD] = brick_lib.Brick(config[BSP_FIELD]).brick_locator
# Create the config file.
workspace_lib.WriteConfigFile(self._path, config)
except (brick_lib.BrickNotFound, workspace_lib.ConfigFileError) as e:
raise BlueprintCreationError('Blueprint creation failed. %s' % e)
def GetBricks(self):
"""Returns the bricks field of a blueprint."""
return self.config.get(BRICKS_FIELD, [])
def GetBSP(self):
"""Returns the BSP field of a blueprint."""
return self.config.get(BSP_FIELD)
def FriendlyName(self):
"""Returns the friendly name for this blueprint."""
return workspace_lib.LocatorToFriendlyName(self._locator)
def GetUsedBricks(self):
"""Returns the set of bricks used by this blueprint."""
brick_map = {}
for top_brick in self.GetBricks() + [self.GetBSP()]:
for b in brick_lib.Brick(top_brick).BrickStack():
brick_map[b.brick_locator] = b
return brick_map.values()