summaryrefslogtreecommitdiff
path: root/src/buildstream/plugins/sources/local.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/buildstream/plugins/sources/local.py')
-rw-r--r--src/buildstream/plugins/sources/local.py147
1 files changed, 147 insertions, 0 deletions
diff --git a/src/buildstream/plugins/sources/local.py b/src/buildstream/plugins/sources/local.py
new file mode 100644
index 000000000..50df85427
--- /dev/null
+++ b/src/buildstream/plugins/sources/local.py
@@ -0,0 +1,147 @@
+#
+# Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
+#
+# Authors:
+# Tristan Van Berkom <tristan.vanberkom@codethink.co.uk>
+# Tiago Gomes <tiago.gomes@codethink.co.uk>
+
+"""
+local - stage local files and directories
+=========================================
+
+**Usage:**
+
+.. code:: yaml
+
+ # Specify the local source kind
+ kind: local
+
+ # Specify the project relative path to a file or directory
+ path: files/somefile.txt
+
+See :ref:`built-in functionality doumentation <core_source_builtins>` for
+details on common configuration options for sources.
+"""
+
+import os
+import stat
+from buildstream import Source, Consistency
+from buildstream import utils
+
+
+class LocalSource(Source):
+ # pylint: disable=attribute-defined-outside-init
+
+ 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
+
+ def configure(self, node):
+ self.node_validate(node, ['path', *Source.COMMON_CONFIG_KEYS])
+ self.path = self.node_get_project_path(node, 'path')
+ self.fullpath = os.path.join(self.get_project_directory(), self.path)
+
+ def preflight(self):
+ pass
+
+ def get_unique_key(self):
+ if self.__unique_key is None:
+ # Get a list of tuples of the the project relative paths and fullpaths
+ if os.path.isdir(self.fullpath):
+ filelist = utils.list_relative_paths(self.fullpath)
+ filelist = [(relpath, os.path.join(self.fullpath, relpath)) for relpath in filelist]
+ else:
+ filelist = [(self.path, self.fullpath)]
+
+ # Return a list of (relative filename, sha256 digest) tuples, a sorted list
+ # has already been returned by list_relative_paths()
+ self.__unique_key = [(relpath, unique_key(fullpath)) for relpath, fullpath in filelist]
+ return self.__unique_key
+
+ def get_consistency(self):
+ return Consistency.CACHED
+
+ # We dont have a ref, we're a local file...
+ def load_ref(self, node):
+ pass
+
+ def get_ref(self):
+ return None # pragma: nocover
+
+ def set_ref(self, ref, node):
+ pass # pragma: nocover
+
+ def fetch(self):
+ # Nothing to do here for a local source
+ pass # pragma: nocover
+
+ def stage(self, directory):
+
+ # Dont use hardlinks to stage sources, they are not write protected
+ # in the sandbox.
+ with self.timed_activity("Staging local files at {}".format(self.path)):
+
+ if os.path.isdir(self.fullpath):
+ files = list(utils.list_relative_paths(self.fullpath))
+ utils.copy_files(self.fullpath, directory)
+ else:
+ destfile = os.path.join(directory, os.path.basename(self.path))
+ files = [os.path.basename(self.path)]
+ utils.safe_copy(self.fullpath, destfile)
+
+ for f in files:
+ # Non empty directories are not listed by list_relative_paths
+ dirs = f.split(os.sep)
+ for i in range(1, len(dirs)):
+ d = os.path.join(directory, *(dirs[:i]))
+ assert os.path.isdir(d) and not os.path.islink(d)
+ os.chmod(d, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
+
+ path = os.path.join(directory, f)
+ if os.path.islink(path):
+ pass
+ elif os.path.isdir(path):
+ os.chmod(path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
+ else:
+ st = os.stat(path)
+ if st.st_mode & stat.S_IXUSR:
+ os.chmod(path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
+ else:
+ os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
+
+ 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 LocalSource