| # 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() |