#!/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 import os from . import ImplError from . import _yaml class Source(): """Source() Base Source class. All Sources derive from this class, this interface defines how the core will be interacting with Sources. """ def __init__(self, context, project, meta): self.__context = context # The Context object self.__project = project # The Project object self.__directory = meta.directory # Staging relative directory self.__origin_node = meta.origin_node # YAML node this Source was loaded from self.__origin_toplevel = meta.origin_toplevel # Toplevel YAML node for the file self.__origin_filename = meta.origin_filename # Filename of the file the source was loaded from self.__provenance = _yaml.node_get_provenance(meta.config) # Provenance information self.configure(meta.config) # Source implementations may stringify themselves for the purpose of logging and errors def __str__(self): return "%s source declared at %s" % (self.get_kind(), str(self.__provenance)) def get_kind(self): """Fetches kind of this source Returns: (str): The kind of this source """ modulename = type(self).__module__ return modulename.split('.')[-1] def get_mirror_directory(self): """Fetches the directory where this source should store things Returns: (str): The directory belonging to this source """ # Create the directory if it doesnt exist directory = os.path.join(self.__context.sourcedir, self.get_kind()) os.makedirs(directory, exist_ok=True) return directory def get_context(self): """Fetches the context Returns: (object): The :class:`.Context` """ return self.__context def get_project(self): """Fetches the project Returns: (object): The :class:`.Project` """ return self.__project def configure(self, node): """Configure the Source from loaded configuration data Args: node (dict): The loaded configuration dictionary Raises: :class:`.SourceError` :class:`.LoadError` Source implementors should implement this method to read configuration data and store it. Use of the the :func:`~buildstream.utils.node_get_member` convenience method will ensure that a nice :class:`.LoadError` is triggered whenever the YAML input configuration is faulty. Implementations may raise :class:`.SourceError` for any other error. """ raise ImplError("Source plugin '%s' does not implement configure()" % self.get_kind()) def preflight(self): """Preflight Check Raises: :class:`.SourceError` :class:`.ProgramNotFoundError` The method is run during pipeline preflight check, sources should use this method to determine if they are able to function in the host environment or if the data is unsuitable. Sources should check for the presence of any host tooling they may require to fetch source code using :func:`.utils.get_host_tool` which will raise :class:`.ProgramNotFoundError` automatically. Implementors should simply raise :class:`.SourceError` with an informative message in the case that the host environment is unsuitable for operation. """ raise ImplError("Source plugin '%s' does not implement preflight()" % self.get_kind()) def refresh(self, node): """Refresh specific source references Args: node (dict): The same dictionary which was previously passed to :func:`~buildstream.source.Source.configure` Raises: :class:`.SourceError` Sources which implement some revision control system should implement this by updating the commit reference from a symbolic tracking branch or tag. The commit reference should be updated internally on the given Source object and also in the passed *node* parameter so that a user's project may optionally be updated with the new reference. Sources which implement a tarball or file should implement this by updating an sha256 sum. Implementors should raise :class:`.SourceError` if some error is encountered while attempting to refresh. """ raise ImplError("Source plugin '%s' does not implement refresh()" % self.get_kind()) def get_unique_key(self): """Return something which uniquely identifies the source Returns: A string, list or dictionary which uniquely identifies the sources to use Implementors can usually implement this by returning a string which the uniquely depicts the source, for instance a git sha or an sha256 sum of a tarball. Implementors can expect that by this time an exact source reference has been obtained as we have passed the :func:`~buildstream.source.Source.preflight` stage and :func:`~buildstream.source.Source.refresh` was called if necessary. """ raise ImplError("Source plugin '%s' does not implement get_unique_key()" % self.get_kind()) def fetch(self): """Fetch remote sources and mirror them locally, ensuring at least that the specific reference is cached locally. Raises: :class:`.SourceError` Implementors should raise :class:`.SourceError` if the there is some network error or if the source reference could not be matched. """ raise ImplError("Source plugin '%s' does not implement fetch()" % self.get_kind()) def stage(self, directory): """Stage the sources to a directory Args: directory (str): Path to stage the source Raises: :class:`.SourceError` Implementors should assume that *directory* already exists and stage already cached sources to the passed directory. Implementors should raise :class:`.SourceError` when encountering some system error. """ raise ImplError("Source plugin '%s' does not implement stage()" % self.get_kind())