... |
... |
@@ -37,7 +37,7 @@ from .._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 |
37
|
37
|
from .._exceptions import BstError
|
38
|
38
|
from .directory import Directory, VirtualDirectoryError
|
39
|
39
|
from ._filebaseddirectory import FileBasedDirectory
|
40
|
|
-from ..utils import FileListResult, safe_copy, list_relative_paths
|
|
40
|
+from ..utils import FileListResult, safe_copy, list_relative_paths, _relative_symlink_target
|
41
|
41
|
from .._artifactcache.cascache import CASCache
|
42
|
42
|
|
43
|
43
|
|
... |
... |
@@ -286,6 +286,71 @@ class CasBasedDirectory(Directory): |
286
|
286
|
directory = directory.descend(c, create=True)
|
287
|
287
|
return directory
|
288
|
288
|
|
|
289
|
+ def _resolve(self, name, absolute_symlinks_resolve=True):
|
|
290
|
+ """ Resolves any name to an object. If the name points to a symlink in this
|
|
291
|
+ directory, it returns the thing it points to, recursively. Returns a CasBasedDirectory, FileNode or None. Never creates a directory or otherwise alters the directory. """
|
|
292
|
+ # First check if it's a normal object and return that
|
|
293
|
+
|
|
294
|
+ if name not in self.index:
|
|
295
|
+ return None
|
|
296
|
+ index_entry = self.index[name]
|
|
297
|
+ if isinstance(index_entry.buildstream_object, Directory):
|
|
298
|
+ return index_entry.buildstream_object
|
|
299
|
+ elif isinstance(index_entry.pb_object, remote_execution_pb2.FileNode):
|
|
300
|
+ return index_entry.pb_object
|
|
301
|
+
|
|
302
|
+ assert isinstance(index_entry.pb_object, remote_execution_pb2.SymlinkNode)
|
|
303
|
+ symlink = index_entry.pb_object
|
|
304
|
+ components = symlink.target.split(CasBasedDirectory._pb2_path_sep)
|
|
305
|
+
|
|
306
|
+ absolute = symlink.target.startswith(CasBasedDirectory._pb2_absolute_path_prefix)
|
|
307
|
+ if absolute:
|
|
308
|
+ if absolute_symlinks_resolve:
|
|
309
|
+ start_directory = self.find_root()
|
|
310
|
+ # Discard the first empty element
|
|
311
|
+ components.pop(0)
|
|
312
|
+ else:
|
|
313
|
+ print(" _resolve: Absolute symlink, which we won't resolve.")
|
|
314
|
+ return None
|
|
315
|
+ else:
|
|
316
|
+ start_directory = self
|
|
317
|
+ directory = start_directory
|
|
318
|
+ print("Resolve {}: starting from {}".format(symlink.target, start_directory))
|
|
319
|
+ while True:
|
|
320
|
+ if not components:
|
|
321
|
+ # We ran out of path elements and ended up in a directory
|
|
322
|
+ return directory
|
|
323
|
+ c = components.pop(0)
|
|
324
|
+ if c == "..":
|
|
325
|
+ print(" resolving {}: up-dir".format(c))
|
|
326
|
+ # If directory.parent *is* None, this is an attempt to access
|
|
327
|
+ # '..' from the root, which is valid under POSIX; it just
|
|
328
|
+ # returns the root.
|
|
329
|
+ if directory.parent is not None:
|
|
330
|
+ directory = directory.parent
|
|
331
|
+ else:
|
|
332
|
+ if c in directory.index:
|
|
333
|
+ f = directory._resolve(c, absolute_symlinks_resolve)
|
|
334
|
+ # Ultimately f must now be a file or directory
|
|
335
|
+ if isinstance(f, CasBasedDirectory):
|
|
336
|
+ directory = f
|
|
337
|
+ print(" resolving {}: dir".format(c))
|
|
338
|
+
|
|
339
|
+ else:
|
|
340
|
+ # This is a file or None (i.e. broken symlink)
|
|
341
|
+ print(" resolving {}: file/broken link".format(c))
|
|
342
|
+ if components:
|
|
343
|
+ # Oh dear. We have components left to resolve, but the one we're trying to resolve points to a file.
|
|
344
|
+ raise VirtualDirectoryError("Reached a file called {} while trying to resolve a symlink; cannot proceed".format(c))
|
|
345
|
+ else:
|
|
346
|
+ return f
|
|
347
|
+ else:
|
|
348
|
+ print(" resolving {}: nonexistent!".format(c))
|
|
349
|
+ return None
|
|
350
|
+
|
|
351
|
+ # Shouldn't get here.
|
|
352
|
+
|
|
353
|
+
|
289
|
354
|
def _check_replacement(self, name, path_prefix, fileListResult):
|
290
|
355
|
""" Checks whether 'name' exists, and if so, whether we can overwrite it.
|
291
|
356
|
If we can, add the name to 'overwritten_files' and delete the existing entry.
|
... |
... |
@@ -542,21 +607,27 @@ class CasBasedDirectory(Directory): |
542
|
607
|
"""
|
543
|
608
|
|
544
|
609
|
print("Running list_relative_paths on relpath {}".format(relpath))
|
545
|
|
- symlink_list = filter(lambda i: isinstance(i[1].pb_object, remote_execution_pb2.SymlinkNode), self.index.items())
|
546
|
|
- file_list = filter(lambda i: isinstance(i[1].pb_object, remote_execution_pb2.FileNode), self.index.items())
|
|
610
|
+ symlink_list = list(filter(lambda i: isinstance(i[1].pb_object, remote_execution_pb2.SymlinkNode), self.index.items()))
|
|
611
|
+ file_list = list(filter(lambda i: isinstance(i[1].pb_object, remote_execution_pb2.FileNode), self.index.items()))
|
|
612
|
+ directory_list = list(filter(lambda i: isinstance(i[1].buildstream_object, CasBasedDirectory), self.index.items()))
|
547
|
613
|
print("Running list_relative_paths on relpath {}. files={}, symlinks={}".format(relpath, [f[0] for f in file_list], [s[0] for s in symlink_list]))
|
548
|
614
|
|
549
|
615
|
for (k, v) in sorted(symlink_list):
|
550
|
|
- print("Yielding symlink {}".format(k))
|
551
|
|
- yield os.path.join(relpath, k)
|
552
|
|
- for (k, v) in sorted(file_list):
|
553
|
|
- print("Yielding file {}".format(k))
|
554
|
|
- yield os.path.join(relpath, k)
|
555
|
|
- else:
|
|
616
|
+ target = self._resolve(k, absolute_symlinks_resolve=True)
|
|
617
|
+ if isinstance(target, CasBasedDirectory):
|
|
618
|
+ print("Adding the resolved symlink {} which resolves to {} to our directory list".format(k, target))
|
|
619
|
+ directory_list.append((k,IndexEntry(k, buildstream_object=target)))
|
|
620
|
+ else:
|
|
621
|
+ # Broken symlinks are also considered files!
|
|
622
|
+ file_list.append((k,v))
|
|
623
|
+ if file_list == [] and relpath != "":
|
556
|
624
|
print("Yielding empty directory name {}".format(relpath))
|
557
|
625
|
yield relpath
|
|
626
|
+ else:
|
|
627
|
+ for (k, v) in sorted(file_list):
|
|
628
|
+ print("Yielding file {}".format(k))
|
|
629
|
+ yield os.path.join(relpath, k)
|
558
|
630
|
|
559
|
|
- directory_list = filter(lambda i: isinstance(i[1].buildstream_object, CasBasedDirectory), self.index.items())
|
560
|
631
|
for (k, v) in sorted(directory_list):
|
561
|
632
|
print("Yielding from subdirectory name {}".format(k))
|
562
|
633
|
yield from v.buildstream_object.list_relative_paths(relpath=os.path.join(relpath, k))
|