summaryrefslogtreecommitdiff
path: root/src/buildstream/storage/_casbaseddirectory.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/buildstream/storage/_casbaseddirectory.py')
-rw-r--r--src/buildstream/storage/_casbaseddirectory.py71
1 files changed, 46 insertions, 25 deletions
diff --git a/src/buildstream/storage/_casbaseddirectory.py b/src/buildstream/storage/_casbaseddirectory.py
index 51d9909fd..d6c4a7813 100644
--- a/src/buildstream/storage/_casbaseddirectory.py
+++ b/src/buildstream/storage/_casbaseddirectory.py
@@ -31,8 +31,10 @@ import os
import stat
import copy
import tarfile as tarfilelib
+from contextlib import contextmanager
from io import StringIO
+from .. import utils
from .._protos.build.bazel.remote.execution.v2 import remote_execution_pb2
from .directory import Directory, VirtualDirectoryError, _FileType
from ._filebaseddirectory import FileBasedDirectory
@@ -191,8 +193,7 @@ class CasBasedDirectory(Directory):
return newdir
- def _add_file(self, basename, filename, modified=False, can_link=False, properties=None):
- path = os.path.join(basename, filename)
+ def _add_file(self, name, path, modified=False, can_link=False, properties=None):
digest = self.cas_cache.add_object(path=path, link_directly=can_link)
is_executable = os.access(path, os.X_OK)
node_properties = []
@@ -205,21 +206,13 @@ class CasBasedDirectory(Directory):
node_properties.append(node_property)
entry = IndexEntry(
- filename,
+ name,
_FileType.REGULAR_FILE,
digest=digest,
is_executable=is_executable,
- modified=modified or filename in self.index,
+ modified=modified or name in self.index,
node_properties=node_properties,
)
- self.index[filename] = entry
-
- self.__invalidate_digest()
-
- def _create_empty_file(self, name):
- digest = self.cas_cache.add_object(buffer=b"")
-
- entry = IndexEntry(name, _FileType.REGULAR_FILE, digest=digest)
self.index[name] = entry
self.__invalidate_digest()
@@ -314,14 +307,6 @@ class CasBasedDirectory(Directory):
self.__invalidate_digest()
- def find_root(self):
- """ Finds the root of this directory tree by following 'parent' until there is
- no parent. """
- if self.parent:
- return self.parent.find_root()
- else:
- return self
-
def descend(self, *paths, create=False, follow_symlinks=False):
"""Descend one or more levels of directory hierarchy and return a new
Directory object for that directory.
@@ -356,7 +341,7 @@ class CasBasedDirectory(Directory):
linklocation = entry.target
newpaths = linklocation.split(os.path.sep)
if os.path.isabs(linklocation):
- current_dir = current_dir.find_root().descend(*newpaths, follow_symlinks=True)
+ current_dir = current_dir._find_root().descend(*newpaths, follow_symlinks=True)
else:
current_dir = current_dir.descend(*newpaths, follow_symlinks=True)
else:
@@ -517,8 +502,8 @@ class CasBasedDirectory(Directory):
result = FileListResult()
if self._check_replacement(os.path.basename(external_pathspec), os.path.dirname(external_pathspec), result):
self._add_file(
- os.path.dirname(external_pathspec),
os.path.basename(external_pathspec),
+ external_pathspec,
modified=os.path.basename(external_pathspec) in result.overwritten,
properties=properties,
)
@@ -740,6 +725,34 @@ class CasBasedDirectory(Directory):
path += "/" + self.common_name
return path
+ @contextmanager
+ def open_file(self, *path: str, mode: str = "r"):
+ subdir = self.descend(*path[:-1])
+ entry = subdir.index.get(path[-1])
+
+ if entry and entry.type != _FileType.REGULAR_FILE:
+ raise VirtualDirectoryError("{} in {} is not a file".format(path[-1], str(subdir)))
+
+ if mode not in ["r", "rb", "w", "wb", "x", "xb"]:
+ raise ValueError("Unsupported mode: `{}`".format(mode))
+
+ if "r" in mode:
+ if not entry:
+ raise FileNotFoundError("{} not found in {}".format(path[-1], str(subdir)))
+
+ # Read-only access, allow direct access to CAS object
+ with open(self.cas_cache.objpath(entry.digest), mode, encoding="utf-8") as f:
+ yield f
+ else:
+ if "x" in mode and entry:
+ raise FileExistsError("{} already exists in {}".format(path[-1], str(subdir)))
+
+ with utils._tempnamedfile(mode, encoding="utf-8", dir=self.cas_cache.tmpdir) as f:
+ yield f
+ # Import written temporary file into CAS
+ f.flush()
+ subdir._add_file(path[-1], f.name, modified=True)
+
def __str__(self):
return "[CAS:{}]".format(self._get_identifier())
@@ -750,6 +763,14 @@ class CasBasedDirectory(Directory):
"_get_underlying_directory was called on a CAS-backed directory," + " which has no underlying directory."
)
+ def _find_root(self):
+ """ Finds the root of this directory tree by following 'parent' until there is
+ no parent. """
+ if self.parent:
+ return self.parent._find_root()
+ else:
+ return self
+
# _get_digest():
#
# Return the Digest for this directory.
@@ -796,7 +817,7 @@ class CasBasedDirectory(Directory):
return self.__digest
- def _exists(self, *path, follow_symlinks=False):
+ def exists(self, *path, follow_symlinks=False):
try:
subdir = self.descend(*path[:-1], follow_symlinks=follow_symlinks)
target = subdir.index.get(path[-1])
@@ -805,8 +826,8 @@ class CasBasedDirectory(Directory):
linklocation = target.target
newpath = linklocation.split(os.path.sep)
if os.path.isabs(linklocation):
- return subdir.find_root()._exists(*newpath, follow_symlinks=True)
- return subdir._exists(*newpath, follow_symlinks=True)
+ return subdir._find_root().exists(*newpath, follow_symlinks=True)
+ return subdir.exists(*newpath, follow_symlinks=True)
else:
return True
return False