diff options
author | Darius Makovsky <traveltissues@protonmail.com> | 2019-09-05 14:22:19 +0100 |
---|---|---|
committer | Darius Makovsky <traveltissues@protonmail.com> | 2019-09-18 16:38:19 +0100 |
commit | 127245b969af38fb8504fb14ccda60703bb012ac (patch) | |
tree | a638f43a42f5b11fc6007c354f3fadfa8a56ce8d | |
parent | b4f3f3279619d597d3208cfb8e98f223afd64196 (diff) | |
download | buildstream-127245b969af38fb8504fb14ccda60703bb012ac.tar.gz |
workspace.py: add workspace source plugin
The `workspace.init_workspace()` call should wrap
`source._init_workspace` for held sources to support those sources not
publishing `BST_VIRTUAL_DIRECTORY`
-rw-r--r-- | src/buildstream/plugins/sources/workspace.py | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/src/buildstream/plugins/sources/workspace.py b/src/buildstream/plugins/sources/workspace.py new file mode 100644 index 000000000..c9afb6b52 --- /dev/null +++ b/src/buildstream/plugins/sources/workspace.py @@ -0,0 +1,135 @@ +# +# Copyright (C) 2019 Bloomberg Finance LP +# +# 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 <http://www.gnu.org/licenses/>. + +""" +:orphan: + +workspace - stage an opened workspace directory +=============================================== + +**Usage:** + +The workspace plugin must not be directly used. This plugin is used as the +kind for a synthetic node representing the sources of an element with an open +workspace. The node constructed would be specified as follows: + +.. code:: yaml + + # Specify the workspace source kind + kind: workspace + + # Specify the project relative path to a file or directory + path: files/somefile.txt +""" + +import os +from buildstream.storage.directory import Directory +from buildstream import Source, SourceError, Consistency +from buildstream import utils + + +class WorkspaceSource(Source): + # pylint: disable=attribute-defined-outside-init + + BST_STAGE_VIRTUAL_DIRECTORY = True + + def __init__(self, context, project, meta): + super().__init__(context, project, meta) + + # Cached unique key to avoid multiple file system traversal if the unique key is requested multiple times. + self.__unique_key = None + self.__element_sources = [] + + def set_element_sources(self, _element_sources): + self.__element_sources = _element_sources + + def get_element_sources(self): + return self.__element_sources + + def configure(self, node): + node.validate_keys(['path', 'ref', 'kind']) + self.path = self.node_get_project_path(node.get_scalar('path')) + self.ref = node.get_str('ref', None) + self.fullpath = os.path.join(self.get_project_directory(), self.path) + + def preflight(self): + return + + def get_unique_key(self): + return self.__unique_key + + def init_workspace(self, directory): + # for each source held by the workspace we must call init_workspace + # those sources may override `init_workspace` expecting str or Directory + # and this will need to be extracted from the directory passed to this method + assert isinstance(directory, Directory) + directory = directory.external_directory + for source in self.get_element_sources(): + source._init_workspace(directory) + + def get_consistency(self): + if self.ref is None: + return Consistency.INCONSISTENT + + return Consistency.RESOLVED + + def load_ref(self, node): + self.ref = node.get_str('ref', None) + + def get_ref(self): + return self.ref + + def set_ref(self, ref, node): + node['ref'] = self.ref = ref + + def fetch(self): + pass # pragma: nocover + + def stage(self, directory): + # directory should always be a Directory object + assert isinstance(directory, Directory) + with self.timed_activity("Staging local files into CAS"): + if os.path.isdir(self.fullpath) and not os.path.islink(self.fullpath): + result = directory.import_files(self.fullpath) + else: + result = directory.import_single_file(self.fullpath) + + if result.overwritten or result.ignored: + raise SourceError( + "Failed to stage source: files clash with existing directory", + reason='ensure-stage-dir-fail') + + def _get_local_path(self): + return self.fullpath + + +# Create a unique key for a file +def unique_key(filename): + + # Return some hard coded things for files which + # have no content to calculate a key for + if os.path.islink(filename): + # For a symbolic link, use the link target as its unique identifier + return os.readlink(filename) + elif os.path.isdir(filename): + return "0" + + return utils.sha256sum(filename) + + +# Plugin entry point +def setup(): + return WorkspaceSource |