... |
... |
@@ -87,9 +87,7 @@ class CasBasedDirectory(Directory): |
87
|
87
|
if ref:
|
88
|
88
|
with open(self.cas_cache.objpath(ref), 'rb') as f:
|
89
|
89
|
self.pb2_directory.ParseFromString(f.read())
|
90
|
|
- print("Opening ref {} and parsed into directory containing: {} {} {}.".format(ref.hash, [d.name for d in self.pb2_directory.directories],
|
91
|
|
- [d.name for d in self.pb2_directory.symlinks],
|
92
|
|
- [d.name for d in self.pb2_directory.files]))
|
|
90
|
+
|
93
|
91
|
self.ref = ref
|
94
|
92
|
self.index = OrderedDict()
|
95
|
93
|
self.parent = parent
|
... |
... |
@@ -141,41 +139,6 @@ class CasBasedDirectory(Directory): |
141
|
139
|
# We don't need to do anything more than that; files were already added ealier, and symlinks are
|
142
|
140
|
# part of the directory structure.
|
143
|
141
|
|
144
|
|
- def _add_new_blank_directory(self, name) -> Directory:
|
145
|
|
- bst_dir = CasBasedDirectory(self.context, parent=self, filename=name)
|
146
|
|
- new_pb2_dirnode = self.pb2_directory.directories.add()
|
147
|
|
- new_pb2_dirnode.name = name
|
148
|
|
- # Calculate the hash for an empty directory
|
149
|
|
- if name in self.index:
|
150
|
|
- raise VirtualDirectoryError("Creating directory {} would overwrite an existing item in {}"
|
151
|
|
- .format(name, str(self)))
|
152
|
|
- new_pb2_directory = remote_execution_pb2.Directory()
|
153
|
|
- self.cas_cache.add_object(digest=new_pb2_dirnode.digest, buffer=new_pb2_directory.SerializeToString())
|
154
|
|
- self.index[name] = IndexEntry(new_pb2_dirnode, buildstream_object=bst_dir)
|
155
|
|
- return bst_dir
|
156
|
|
-
|
157
|
|
- def create_directory(self, name: str) -> Directory:
|
158
|
|
- """Creates a directory if it does not already exist. This does not
|
159
|
|
- cause an error if something exists; it will remove files and
|
160
|
|
- symlinks to files which have the same name in this
|
161
|
|
- directory. Symlinks to directories with the name 'name' are
|
162
|
|
- unaltered; it's assumed that the target of that symlink will
|
163
|
|
- be used.
|
164
|
|
-
|
165
|
|
- """
|
166
|
|
- existing_item = self._find_pb2_entry(name)
|
167
|
|
- if isinstance(existing_item, remote_execution_pb2.FileNode):
|
168
|
|
- # Directory imported over file with same name
|
169
|
|
- self.delete_entry(name)
|
170
|
|
- elif isinstance(existing_item, remote_execution_pb2.SymlinkNode):
|
171
|
|
- # Directory imported over symlink with same source name
|
172
|
|
- if self.symlink_target_is_directory(existing_item):
|
173
|
|
- return self._resolve_symlink_or_directory(name) # That's fine; any files in the source directory should end up at the target of the symlink.
|
174
|
|
- else:
|
175
|
|
- self.delete_entry(name) # Symlinks to files get replaced
|
176
|
|
- return self.descend(name, create=True) # Creates the directory if it doesn't already exist.
|
177
|
|
-
|
178
|
|
-
|
179
|
142
|
def _find_pb2_entry(self, name):
|
180
|
143
|
if name in self.index:
|
181
|
144
|
return self.index[name].pb_object
|
... |
... |
@@ -217,16 +180,8 @@ class CasBasedDirectory(Directory): |
217
|
180
|
filenode.is_executable = is_executable
|
218
|
181
|
self.index[filename] = IndexEntry(filenode, modified=(filename in self.index))
|
219
|
182
|
|
220
|
|
- def _add_new_link(self, basename, filename):
|
221
|
|
- existing_link = self._find_pb2_entry(filename)
|
222
|
|
- if existing_link:
|
223
|
|
- symlinknode = existing_link
|
224
|
|
- else:
|
225
|
|
- symlinknode = self.pb2_directory.symlinks.add()
|
226
|
|
- symlinknode.name = filename
|
227
|
|
- # A symlink node has no digest.
|
228
|
|
- symlinknode.target = os.readlink(os.path.join(basename, filename))
|
229
|
|
- self.index[filename] = IndexEntry(symlinknode, modified=(existing_link is not None))
|
|
183
|
+ def _copy_link_from_filesystem(self, basename, filename):
|
|
184
|
+ self._add_new_link_direct(filename, os.readlink(os.path.join(basename, filename)))
|
230
|
185
|
|
231
|
186
|
def _add_new_link_direct(self, name, target):
|
232
|
187
|
existing_link = self._find_pb2_entry(name)
|
... |
... |
@@ -240,7 +195,6 @@ class CasBasedDirectory(Directory): |
240
|
195
|
symlinknode.target = target
|
241
|
196
|
self.index[name] = IndexEntry(symlinknode, modified=(existing_link is not None))
|
242
|
197
|
|
243
|
|
-
|
244
|
198
|
def delete_entry(self, name):
|
245
|
199
|
for collection in [self.pb2_directory.files, self.pb2_directory.symlinks, self.pb2_directory.directories]:
|
246
|
200
|
for thing in collection:
|
... |
... |
@@ -297,9 +251,6 @@ class CasBasedDirectory(Directory): |
297
|
251
|
else:
|
298
|
252
|
if create:
|
299
|
253
|
newdir = self._add_directory(subdirectory_spec[0])
|
300
|
|
- print("Created new directory called {} and descending into it".format(subdirectory_spec[0]))
|
301
|
|
- #if subdirectory_spec[0] == "broken":
|
302
|
|
- # assert False
|
303
|
254
|
return newdir.descend(subdirectory_spec[1:], create)
|
304
|
255
|
else:
|
305
|
256
|
error = "No entry called '{}' found in {}. There are directories called {}."
|
... |
... |
@@ -316,7 +267,7 @@ class CasBasedDirectory(Directory): |
316
|
267
|
else:
|
317
|
268
|
return self
|
318
|
269
|
|
319
|
|
- def _resolve_symlink_or_directory(self, name):
|
|
270
|
+ def _force_resolve(self, name):
|
320
|
271
|
"""Used only by _import_files_from_directory. Tries to resolve a
|
321
|
272
|
directory name or symlink name. 'name' must be an entry in this
|
322
|
273
|
directory. It must be a single symlink or directory name, not a path
|
... |
... |
@@ -338,28 +289,31 @@ class CasBasedDirectory(Directory): |
338
|
289
|
if isinstance(self.index[name].buildstream_object, Directory):
|
339
|
290
|
return True
|
340
|
291
|
target = self._resolve(name)
|
341
|
|
- print("Is {} followable? Resolved to {}".format(name, target))
|
342
|
|
- return isinstance(target, CasBasedDirectory) or target is None
|
|
292
|
+ return isinstance(target, CasBasedDirectory) or target is None # TODO: But why return True if it's None (broken link/circular loop)? Surely that is against the docstring.
|
343
|
293
|
|
344
|
|
- def _resolve_symlink(self, node, force_create=True):
|
345
|
|
- """Same as _resolve_symlink_or_directory but takes a SymlinkNode.
|
346
|
|
- """
|
347
|
|
- return self._resolve(node.name, force_create=True)
|
348
|
|
-
|
349
|
294
|
def _resolve(self, name, absolute_symlinks_resolve=True, force_create=False, first_seen_object = None):
|
350
|
295
|
""" Resolves any name to an object. If the name points to a symlink in
|
351
|
296
|
this directory, it returns the thing it points to,
|
352
|
|
- recursively. Returns a CasBasedDirectory, FileNode or
|
353
|
|
- None.
|
|
297
|
+ recursively.
|
|
298
|
+
|
|
299
|
+ Returns a CasBasedDirectory, FileNode or
|
|
300
|
+ None. None indicates any of these cases:
|
|
301
|
+ * 'name' does not exist in this directory
|
|
302
|
+ * 'name' is a broken symlink,
|
|
303
|
+ * 'name' points to an infinite symlink loop.
|
|
304
|
+ * 'name' points to an absolute symlink and absolute_symlinks_resolve is False.
|
354
|
305
|
|
355
|
306
|
If force_create is on, will attempt to create directories to make symlinks and directories resolve.
|
356
|
307
|
If force_create is off, this will never alter this directory.
|
357
|
308
|
|
358
|
309
|
"""
|
359
|
|
- # First check if it's a normal object and return that
|
360
|
310
|
|
|
311
|
+ # TODO: first_seen_object isn't sufficient. We could get into a loop after following one link and not detect it.
|
|
312
|
+ # TODO: 'None' is overloaded, maybe we should use exceptions and leave 'None' for actual nonexistent things.
|
361
|
313
|
if name not in self.index:
|
362
|
314
|
return None
|
|
315
|
+
|
|
316
|
+ # First check if it's a normal object and return that
|
363
|
317
|
index_entry = self.index[name]
|
364
|
318
|
if isinstance(index_entry.buildstream_object, Directory):
|
365
|
319
|
return index_entry.buildstream_object
|
... |
... |
@@ -375,7 +329,6 @@ class CasBasedDirectory(Directory): |
375
|
329
|
### Infinite symlink loop detected ###
|
376
|
330
|
return None
|
377
|
331
|
|
378
|
|
- print("Resolving '{}': This is a symlink node in the current directory.".format(name))
|
379
|
332
|
symlink = index_entry.pb_object
|
380
|
333
|
components = symlink.target.split(CasBasedDirectory._pb2_path_sep)
|
381
|
334
|
|
... |
... |
@@ -386,12 +339,12 @@ class CasBasedDirectory(Directory): |
386
|
339
|
# Discard the first empty element
|
387
|
340
|
components.pop(0)
|
388
|
341
|
else:
|
389
|
|
- print(" _resolve: Absolute symlink, which we won't resolve.")
|
|
342
|
+ # Unresolvable absolute symlink
|
390
|
343
|
return None
|
391
|
344
|
else:
|
392
|
345
|
start_directory = self
|
|
346
|
+
|
393
|
347
|
directory = start_directory
|
394
|
|
- print("Resolve {}: starting from {}".format(symlink.target, start_directory))
|
395
|
348
|
while True:
|
396
|
349
|
if not components:
|
397
|
350
|
# We ran out of path elements and ended up in a directory
|
... |
... |
@@ -407,30 +360,37 @@ class CasBasedDirectory(Directory): |
407
|
360
|
# returns the root.
|
408
|
361
|
else:
|
409
|
362
|
if c in directory.index:
|
|
363
|
+ # Recursive resolve and continue
|
410
|
364
|
f = directory._resolve(c, absolute_symlinks_resolve, first_seen_object=first_seen_object)
|
411
|
|
- # Ultimately f must now be a file or directory
|
|
365
|
+
|
412
|
366
|
if isinstance(f, CasBasedDirectory):
|
413
|
367
|
directory = f
|
414
|
|
-
|
|
368
|
+ elif isinstance(f, remote_execution_pb2.FileNode):
|
|
369
|
+ # F is a file
|
|
370
|
+ if components:
|
|
371
|
+ # We have components still to resolve, but one of the path components
|
|
372
|
+ # is a file.
|
|
373
|
+ if force_create:
|
|
374
|
+ self.delete_entry(c)
|
|
375
|
+ directory = directory.descend(c, create=True)
|
|
376
|
+ else:
|
|
377
|
+ return f # TODO: Why return f? We've got components left and hit a file; this should be an error.
|
|
378
|
+ #raise VirtualDirectoryError("Reached a file called {} while trying to resolve a symlink; cannot proceed".format(c))
|
|
379
|
+ else:
|
|
380
|
+ # It's a file, but there's no components left, so just return that.
|
|
381
|
+ return f
|
415
|
382
|
else:
|
416
|
|
- # This is a file or None (i.e. broken symlink)
|
417
|
|
- if f is None and force_create:
|
418
|
|
- directory = directory.descend(c, create=True)
|
419
|
|
- elif components and force_create:
|
420
|
|
- # Oh dear. We have components left to resolve, but the one we're trying to resolve points to a file.
|
421
|
|
- print("Trying to resolve {}, but found {} was a file.".format(symlink.target, c))
|
422
|
|
- self.delete_entry(c)
|
|
383
|
+ # f is none, which covers many cases
|
|
384
|
+ if force_create:
|
423
|
385
|
directory = directory.descend(c, create=True)
|
424
|
|
- #raise VirtualDirectoryError("Reached a file called {} while trying to resolve a symlink; cannot proceed".format(c))
|
425
|
386
|
else:
|
426
|
|
- return f
|
|
387
|
+ return None
|
427
|
388
|
else:
|
428
|
389
|
if force_create:
|
429
|
390
|
directory = directory.descend(c, create=True)
|
430
|
391
|
else:
|
431
|
392
|
return None
|
432
|
|
-
|
433
|
|
- # Shouldn't get here.
|
|
393
|
+ # You can only exit the while loop with a return, so you shouldn't be here.
|
434
|
394
|
|
435
|
395
|
|
436
|
396
|
def _check_replacement(self, name, path_prefix, fileListResult):
|
... |
... |
@@ -471,7 +431,7 @@ class CasBasedDirectory(Directory): |
471
|
431
|
as a directory tree is descended. """
|
472
|
432
|
if directory_name in self.index:
|
473
|
433
|
if self._is_followable(directory_name):
|
474
|
|
- subdir = self._resolve_symlink_or_directory(directory_name)
|
|
434
|
+ subdir = self._force_resolve(directory_name)
|
475
|
435
|
else:
|
476
|
436
|
print("Overwriting unfollowable thing {}".format(directory_name))
|
477
|
437
|
self.delete_entry(directory_name)
|
... |
... |
@@ -504,7 +464,7 @@ class CasBasedDirectory(Directory): |
504
|
464
|
result.combine(subdir_result)
|
505
|
465
|
elif os.path.islink(import_file):
|
506
|
466
|
if self._check_replacement(entry, path_prefix, result):
|
507
|
|
- self._add_new_link(source_directory, entry)
|
|
467
|
+ self._copy_link_from_filesystem(source_directory, entry)
|
508
|
468
|
result.files_written.append(relative_pathname)
|
509
|
469
|
elif os.path.isdir(import_file):
|
510
|
470
|
# A plain directory which already exists isn't a problem; just ignore it.
|
... |
... |
@@ -517,47 +477,7 @@ class CasBasedDirectory(Directory): |
517
|
477
|
return result
|
518
|
478
|
|
519
|
479
|
|
520
|
|
- def _save(self, name):
|
521
|
|
- """ Saves this directory into the content cache as a named ref. This function is not
|
522
|
|
- currently in use, but may be useful later. """
|
523
|
|
- self._recalculate_recursing_up()
|
524
|
|
- self._recalculate_recursing_down()
|
525
|
|
- (rel_refpath, refname) = os.path.split(name)
|
526
|
|
- refdir = os.path.join(self.cas_directory, 'refs', 'heads', rel_refpath)
|
527
|
|
- refname = os.path.join(refdir, refname)
|
528
|
|
-
|
529
|
|
- if not os.path.exists(refdir):
|
530
|
|
- os.makedirs(refdir)
|
531
|
|
- with open(refname, "wb") as f:
|
532
|
|
- f.write(self.ref.SerializeToString())
|
533
|
|
-
|
534
|
|
- def find_updated_files(self, modified_directory, prefix=""):
|
535
|
|
- """Find the list of written and overwritten files that would result
|
536
|
|
- from importing 'modified_directory' into this one. This does
|
537
|
|
- not change either directory. The reason this exists is for
|
538
|
|
- direct imports of cas directories into other ones, which can
|
539
|
|
- be done by simply replacing a hash, but we still need the file
|
540
|
|
- lists.
|
541
|
|
-
|
542
|
|
- """
|
543
|
|
- result = FileListResult()
|
544
|
|
- for entry in modified_directory.pb2_directory.directories:
|
545
|
|
- existing_dir = self._find_pb2_entry(entry.name)
|
546
|
|
- if existing_dir:
|
547
|
|
- updates_files = existing_dir.find_updated_files(modified_directory.descend(entry.name),
|
548
|
|
- os.path.join(prefix, entry.name))
|
549
|
|
- result.combine(updated_files)
|
550
|
|
- else:
|
551
|
|
- for f in source_directory.descend(entry.name).list_relative_paths():
|
552
|
|
- result.files_written.append(os.path.join(prefix, f))
|
553
|
|
- # None of these can overwrite anything, since the original files don't exist
|
554
|
|
- for entry in modified_directory.pb2_directory.files + modified_directory.pb2_directory.symlinks:
|
555
|
|
- if self._find_pb2_entry(entry.name):
|
556
|
|
- result.files_overwritten.apppend(os.path.join(prefix, entry.name))
|
557
|
|
- result.file_written.apppend(os.path.join(prefix, entry.name))
|
558
|
|
- return result
|
559
|
|
-
|
560
|
|
- def files_in_subdir(sorted_files, dirname):
|
|
480
|
+ def _files_in_subdir(sorted_files, dirname):
|
561
|
481
|
"""Filters sorted_files and returns only the ones which have
|
562
|
482
|
'dirname' as a prefix, with that prefix removed.
|
563
|
483
|
|
... |
... |
@@ -566,9 +486,6 @@ class CasBasedDirectory(Directory): |
566
|
486
|
dirname += os.path.sep
|
567
|
487
|
return [f[len(dirname):] for f in sorted_files if f.startswith(dirname)]
|
568
|
488
|
|
569
|
|
- def symlink_target_is_directory(self, symlink_node):
|
570
|
|
- x = self._resolve_symlink(symlink_node, force_create=False)
|
571
|
|
- return isinstance(x, CasBasedDirectory)
|
572
|
489
|
|
573
|
490
|
def _partial_import_cas_into_cas(self, source_directory, files, path_prefix="", file_list_required=True):
|
574
|
491
|
""" Import only the files and symlinks listed in 'files' from source_directory to this one.
|
... |
... |
@@ -589,7 +506,7 @@ class CasBasedDirectory(Directory): |
589
|
506
|
dirname = components[0]
|
590
|
507
|
if dirname not in processed_directories:
|
591
|
508
|
# Now strip off the first directory name and import files recursively.
|
592
|
|
- subcomponents = CasBasedDirectory.files_in_subdir(files, dirname)
|
|
509
|
+ subcomponents = CasBasedDirectory._files_in_subdir(files, dirname)
|
593
|
510
|
# We will fail at this point if there is a file or symlink to file called 'dirname'.
|
594
|
511
|
if dirname in self.index:
|
595
|
512
|
x = self._resolve(dirname, force_create=True)
|
... |
... |
@@ -600,8 +517,7 @@ class CasBasedDirectory(Directory): |
600
|
517
|
else:
|
601
|
518
|
dest_subdir = x
|
602
|
519
|
else:
|
603
|
|
- self.create_directory(dirname)
|
604
|
|
- dest_subdir = self._resolve_symlink_or_directory(dirname)
|
|
520
|
+ dest_subdir = self.descend(dirname, create=True)
|
605
|
521
|
src_subdir = source_directory.descend(dirname)
|
606
|
522
|
import_result = dest_subdir._partial_import_cas_into_cas(src_subdir, subcomponents,
|
607
|
523
|
path_prefix=fullname, file_list_required=file_list_required)
|
... |
... |
@@ -622,7 +538,7 @@ class CasBasedDirectory(Directory): |
622
|
538
|
# There's either a symlink (valid or not) or existing directory with this name, so do nothing.
|
623
|
539
|
pass
|
624
|
540
|
else:
|
625
|
|
- self.create_directory(f)
|
|
541
|
+ self.descend(f, create=True)
|
626
|
542
|
else:
|
627
|
543
|
# We're importing a file or symlink - replace anything with the same name.
|
628
|
544
|
print("Import of file/symlink {} into this directory. Removing anything existing...".format(f))
|
... |
... |
@@ -639,49 +555,11 @@ class CasBasedDirectory(Directory): |
639
|
555
|
self._add_new_link_direct(name=f, target=item.target)
|
640
|
556
|
return result
|
641
|
557
|
|
642
|
|
- def transfer_node_contents(destination, source):
|
643
|
|
- """Transfers all fields from the source PB2 node into the
|
644
|
|
- destination. Destination and source must be of the same type and must
|
645
|
|
- be a FileNode, SymlinkNode or DirectoryNode.
|
646
|
|
- """
|
647
|
|
- assert(type(destination) == type(source))
|
648
|
|
- destination.name = source.name
|
649
|
|
- if isinstance(destination, remote_execution_pb2.FileNode):
|
650
|
|
- destination.digest.hash = source.digest.hash
|
651
|
|
- destination.digest.size_bytes = source.digest.size_bytes
|
652
|
|
- destination.is_executable = source.is_executable
|
653
|
|
- elif isinstance(destination, remote_execution_pb2.SymlinkNode):
|
654
|
|
- destination.target = source.target
|
655
|
|
- elif isinstance(destination, remote_execution_pb2.DirectoryNode):
|
656
|
|
- destination.digest.hash = source.digest.hash
|
657
|
|
- destination.digest.size_bytes = source.digest.size_bytes
|
658
|
|
- else:
|
659
|
|
- raise VirtualDirectoryError("Incompatible type '{}' used as destination for transfer_node_contents"
|
660
|
|
- .format(destination.type))
|
661
|
|
-
|
662
|
|
- def _add_directory_from_node(self, source_node, source_casdir, can_hardlink=False):
|
663
|
|
- # Duplicate the given node and add it to our index with a CasBasedDirectory object.
|
664
|
|
- # No existing entry with the source node's name can exist.
|
665
|
|
- # source_casdir is only needed if can_hardlink is True.
|
666
|
|
- assert(self._find_pb2_entry(source_node.name) is None)
|
667
|
|
-
|
668
|
|
- if can_hardlink:
|
669
|
|
- new_dir_node = self.pb2_directory.directories.add()
|
670
|
|
- CasBasedDirectory.transfer_node_contents(new_dir_node, source_node)
|
671
|
|
- self.index[source_node.name] = IndexEntry(source_node, buildstream_object=source_casdir, modified=True)
|
672
|
|
- else:
|
673
|
|
- new_dir_node = self.pb2_directory.directories.add()
|
674
|
|
- CasBasedDirectory.transfer_node_contents(new_dir_node, source_node)
|
675
|
|
- buildStreamDirectory = CasBasedDirectory(self.context, ref=source_node.digest,
|
676
|
|
- parent=self, filename=source_node.name)
|
677
|
|
- self.index[source_node.name] = IndexEntry(source_node, buildstream_object=buildStreamDirectory, modified=True)
|
678
|
|
-
|
679
|
558
|
def _import_cas_into_cas(self, source_directory, files=None):
|
680
|
559
|
""" A full import is significantly quicker than a partial import, because we can just
|
681
|
560
|
replace one directory with another's hash, without doing any recursion.
|
682
|
561
|
"""
|
683
|
|
- if files is None:
|
684
|
|
- files = source_directory.list_relative_paths()
|
|
562
|
+
|
685
|
563
|
# You must pass a list into _partial_import (not a generator)
|
686
|
564
|
return self._partial_import_cas_into_cas(source_directory, list(files))
|
687
|
565
|
|
... |
... |
@@ -771,27 +649,27 @@ class CasBasedDirectory(Directory): |
771
|
649
|
can_link (bool): Ignored, since hard links do not have any meaning within CAS.
|
772
|
650
|
"""
|
773
|
651
|
|
774
|
|
- print("Directory before import: {}".format(self.show_files_recursive()))
|
775
|
|
-
|
776
|
|
- if isinstance(external_pathspec, CasBasedDirectory):
|
777
|
|
- print("-"*80 + "Performing direct CAS-to-CAS import")
|
778
|
|
- result = self._import_cas_into_cas(external_pathspec, files=files)
|
779
|
|
- print("Result of cas-to-cas import: {}".format(self.show_files_recursive()))
|
780
|
|
- else:
|
781
|
|
- print("-"*80 + "Performing initial import")
|
782
|
|
- if isinstance(external_pathspec, FileBasedDirectory):
|
783
|
|
- source_directory = external_pathspec.get_underlying_directory()
|
784
|
|
- else:
|
785
|
|
- source_directory = external_pathspec
|
786
|
|
- if files is None:
|
|
652
|
+ if files is None:
|
|
653
|
+ if isinstance(external_pathspec, str):
|
787
|
654
|
files = list_relative_paths(external_pathspec)
|
|
655
|
+ else:
|
|
656
|
+ assert isinstance(external_pathspec, Directory)
|
|
657
|
+ files = external_pathspec.list_relative_paths()
|
|
658
|
+
|
|
659
|
+ if isinstance(external_pathspec, FileBasedDirectory):
|
|
660
|
+ source_directory = external_pathspec.get_underlying_directory()
|
|
661
|
+ result = self._import_files_from_directory(source_directory, files=files)
|
|
662
|
+ elif isinstance(external_pathspec, str):
|
|
663
|
+ source_directory = external_pathspec
|
788
|
664
|
result = self._import_files_from_directory(source_directory, files=files)
|
|
665
|
+ else:
|
|
666
|
+ assert isinstance(external_pathspec, CasBasedDirectory)
|
|
667
|
+ result = self._import_cas_into_cas(external_pathspec, files=files)
|
789
|
668
|
|
790
|
669
|
# TODO: No notice is taken of report_written, update_utimes or can_link.
|
791
|
670
|
# Current behaviour is to fully populate the report, which is inefficient,
|
792
|
671
|
# but still correct.
|
793
|
672
|
|
794
|
|
-
|
795
|
673
|
# We need to recalculate and store the hashes of all directories both
|
796
|
674
|
# up and down the tree; we have changed our directory by importing files
|
797
|
675
|
# which changes our hash and all our parents' hashes of us. The trees
|
... |
... |
@@ -919,12 +797,6 @@ class CasBasedDirectory(Directory): |
919
|
797
|
filelist.append(k)
|
920
|
798
|
return filelist
|
921
|
799
|
|
922
|
|
- def _contains_only_directories(self):
|
923
|
|
- for (k, v) in self.index.items():
|
924
|
|
- if not isinstance(v.buildstream_object, CasBasedDirectory):
|
925
|
|
- return False
|
926
|
|
- return True
|
927
|
|
-
|
928
|
800
|
def list_relative_paths(self, relpath=""):
|
929
|
801
|
"""Provide a list of all relative paths.
|
930
|
802
|
|