summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarius Makovsky <traveltissues@protonmail.com>2019-09-05 14:22:19 +0100
committerDarius Makovsky <traveltissues@protonmail.com>2019-09-18 16:38:19 +0100
commit127245b969af38fb8504fb14ccda60703bb012ac (patch)
treea638f43a42f5b11fc6007c354f3fadfa8a56ce8d
parentb4f3f3279619d597d3208cfb8e98f223afd64196 (diff)
downloadbuildstream-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.py135
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