#!/usr/bin/env python3 # # Copyright (C) 2016 Codethink Limited # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . # # Authors: # Tristan Van Berkom """Loaded Project Configuration The :class:`.Project` object holds all of the project settings from the project configuration file including the project directory it was loaded from. """ import os import multiprocessing # for cpu_count() from collections import Mapping from . import utils from . import _site from . import _yaml from . import _loader # For resolve_arch() from ._profile import Topics, profile_start, profile_end # The separator we use for user specified aliases _ALIAS_SEPARATOR = ':' class Project(): """Project Configuration Args: directory (str): The project directory arch (str): Symbolic machine architecture name Raises: :class:`.LoadError` """ def __init__(self, directory, arch): self.name = None """str: The project name""" self.directory = os.path.abspath(directory) """str: The project directory""" self.element_path = None """str: Absolute path to where elements are loaded from within the project""" self._variables = {} # The default variables overridden with project wide overrides self._environment = {} # The base sandbox environment self._elements = {} # Element specific configurations self._aliases = {} # Aliases dictionary self._plugin_source_paths = [] # Paths to custom sources self._plugin_element_paths = [] # Paths to custom plugins self._cache_key = None profile_start(Topics.LOAD_PROJECT, self.directory.replace(os.sep, '-')) self._load(arch) profile_end(Topics.LOAD_PROJECT, self.directory.replace(os.sep, '-')) def translate_url(self, url): """Translates the given url which may be specified with an alias into a fully qualified url. Args: url (str): A url, which may be using an alias Returns: str: The fully qualified url, with aliases resolved This method is provided for :class:`.Source` objects to resolve fully qualified urls based on the shorthand which is allowed to be specified in the YAML """ if url and _ALIAS_SEPARATOR in url: url_alias, url_body = url.split(_ALIAS_SEPARATOR, 1) alias_url = self._aliases.get(url_alias) if alias_url: url = alias_url + url_body return url # _load(): # # Loads the project configuration file in the project directory # and extracts some things. # # Raises: LoadError if there was a problem with the project.conf # def _load(self, arch): # Load builtin default projectfile = os.path.join(self.directory, "project.conf") config = _yaml.load(_site.default_project_config) # Special variables which have a computed default value must # be processed here before compositing any overrides variables = _yaml.node_get(config, Mapping, 'variables') variables['max-jobs'] = multiprocessing.cpu_count() variables['bst-arch'] = arch # Load project local config and override the builtin project_conf = _yaml.load(projectfile) _yaml.composite(config, project_conf, typesafe=True) # Resolve arches keyword, project may have arch conditionals _loader.resolve_arch(config, arch) # The project name self.name = _yaml.node_get(config, str, 'name') # Load the plugin paths plugins = _yaml.node_get(config, Mapping, 'plugins', default_value={}) self._plugin_source_paths = [os.path.join(self.directory, path) for path in self._extract_plugin_paths(plugins, 'sources')] self._plugin_element_paths = [os.path.join(self.directory, path) for path in self._extract_plugin_paths(plugins, 'elements')] # Resolve element base path elt_path = _yaml.node_get(config, str, 'element-path') self.element_path = os.path.join(self.directory, elt_path) # Source url aliases self._aliases = _yaml.node_get(config, Mapping, 'aliases', default_value={}) # Load base variables self._variables = _yaml.node_get(config, Mapping, 'variables') # Load sandbox configuration self._environment = _yaml.node_get(config, Mapping, 'environment') self._env_nocache = _yaml.node_get(config, list, 'environment-nocache') # Load project split rules self._splits = _yaml.node_get(config, Mapping, 'split-rules') # Element configurations self._elements = _yaml.node_get(config, Mapping, 'elements', default_value={}) def _extract_plugin_paths(self, node, name): if not node: return path_list = _yaml.node_get(node, list, name, default_value=[]) for i in range(len(path_list)): path = _yaml.node_get(node, str, name, indices=[i]) yield path # _get_cache_key(): # # Returns the cache key, calculating it if necessary # # Returns: # (str): A hex digest cache key for the Context # def _get_cache_key(self): if self._cache_key is None: # Anything that alters the build goes into the unique key # (currently nothing here) self._cache_key = utils._generate_key({}) return self._cache_key