diff options
6 files changed, 79 insertions, 1 deletions
diff --git a/src/buildstream/storage/_casbaseddirectory.py b/src/buildstream/storage/_casbaseddirectory.py index ea4862e50..7bd9ceea0 100644 --- a/src/buildstream/storage/_casbaseddirectory.py +++ b/src/buildstream/storage/_casbaseddirectory.py @@ -28,6 +28,9 @@ See also: :ref:`sandboxing`. """ import os +import stat +import tarfile as tarfilelib +from io import StringIO from .._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 from .directory import Directory, VirtualDirectoryError, _FileType @@ -413,7 +416,33 @@ class CasBasedDirectory(Directory): self.cas_cache.checkout(to_directory, self._get_digest(), can_link=can_link) def export_to_tar(self, tarfile, destination_dir, mtime=BST_ARBITRARY_TIMESTAMP): - raise NotImplementedError() + for filename, entry in self.index.items(): + arcname = os.path.join(destination_dir, filename) + if entry.type == _FileType.DIRECTORY: + tarinfo = tarfilelib.TarInfo(arcname) + tarinfo.mtime = mtime + tarinfo.type = tarfilelib.DIRTYPE + tarinfo.mode = 0o755 + tarfile.addfile(tarinfo) + self.descend(filename).export_to_tar(tarfile, arcname, mtime) + elif entry.type == _FileType.REGULAR_FILE: + source_name = self.cas_cache.objpath(entry.digest) + tarinfo = tarfilelib.TarInfo(arcname) + tarinfo.mtime = mtime + tarinfo.mode |= entry.is_executable & stat.S_IXUSR + tarinfo.size = os.path.getsize(source_name) + with open(source_name, "rb") as f: + tarfile.addfile(tarinfo, f) + elif entry.type == _FileType.SYMLINK: + tarinfo = tarfilelib.TarInfo(arcname) + tarinfo.mtime = mtime + tarinfo.mode |= entry.is_executable & stat.S_IXUSR + tarinfo.linkname = entry.target + tarinfo.type = tarfilelib.SYMTYPE + f = StringIO(entry.target) + tarfile.addfile(tarinfo, f) + else: + raise VirtualDirectoryError("can not export file type {} to tar".format(entry.type)) def _mark_changed(self): """ It should not be possible to externally modify a CAS-based diff --git a/tests/frontend/buildcheckout.py b/tests/frontend/buildcheckout.py index a3f68c031..dd4a461ea 100644 --- a/tests/frontend/buildcheckout.py +++ b/tests/frontend/buildcheckout.py @@ -425,6 +425,49 @@ def test_build_checkout_tarball_is_deterministic(datafiles, cli): @pytest.mark.datafiles(DATA_DIR) +def test_build_checkout_tarball_links(datafiles, cli): + project = str(datafiles) + checkout = os.path.join(cli.directory, 'checkout.tar') + extract = os.path.join(cli.directory, 'extract') + + result = cli.run(project=project, args=['build', 'import-links.bst']) + result.assert_success() + + builddir = os.path.join(cli.directory, 'build') + assert os.path.isdir(builddir) + assert not os.listdir(builddir) + + checkout_args = ['artifact', 'checkout', '--tar', checkout, 'import-links.bst'] + + result = cli.run(project=project, args=checkout_args) + result.assert_success() + + tar = tarfile.open(name=checkout, mode="r:") + tar.extractall(extract) + assert open(os.path.join(extract, 'basicfolder', 'basicsymlink')).read() == "file contents\n" + + +@pytest.mark.datafiles(DATA_DIR) +def test_build_checkout_links(datafiles, cli): + project = str(datafiles) + checkout = os.path.join(cli.directory, 'checkout') + + result = cli.run(project=project, args=['build', 'import-links.bst']) + result.assert_success() + + builddir = os.path.join(cli.directory, 'build') + assert os.path.isdir(builddir) + assert not os.listdir(builddir) + + checkout_args = ['artifact', 'checkout', '--directory', checkout, 'import-links.bst'] + + result = cli.run(project=project, args=checkout_args) + result.assert_success() + + assert open(os.path.join(checkout, 'basicfolder', 'basicsymlink')).read() == "file contents\n" + + +@pytest.mark.datafiles(DATA_DIR) @pytest.mark.parametrize("hardlinks", [("copies"), ("hardlinks")]) def test_build_checkout_nonempty(datafiles, cli, hardlinks): project = str(datafiles) diff --git a/tests/frontend/project/elements/import-links.bst b/tests/frontend/project/elements/import-links.bst new file mode 100644 index 000000000..42b279ee2 --- /dev/null +++ b/tests/frontend/project/elements/import-links.bst @@ -0,0 +1,4 @@ +kind: import +sources: +- kind: local + path: files/files-and-links diff --git a/tests/frontend/project/files/files-and-links/basicfile b/tests/frontend/project/files/files-and-links/basicfile new file mode 100644 index 000000000..d03e2425c --- /dev/null +++ b/tests/frontend/project/files/files-and-links/basicfile @@ -0,0 +1 @@ +file contents diff --git a/tests/frontend/project/files/files-and-links/basicfolder/basicsymlink b/tests/frontend/project/files/files-and-links/basicfolder/basicsymlink new file mode 120000 index 000000000..e2b4f7423 --- /dev/null +++ b/tests/frontend/project/files/files-and-links/basicfolder/basicsymlink @@ -0,0 +1 @@ +../basicfile
\ No newline at end of file diff --git a/tests/frontend/project/files/files-and-links/basicfolder/subdir-file b/tests/frontend/project/files/files-and-links/basicfolder/subdir-file new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/frontend/project/files/files-and-links/basicfolder/subdir-file |