You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildstream.apache.org by ju...@apache.org on 2022/07/08 06:00:38 UTC

[buildstream] 01/01: Clean the build directory after a successful build

This is an automated email from the ASF dual-hosted git repository.

juergbi pushed a commit to branch juerg/clean-build-directory
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 1232bbe5ca4b27ec025943719572e0bce5f05f28
Author: Jürg Billeter <j...@bitron.ch>
AuthorDate: Thu Jul 7 22:08:04 2022 +0200

    Clean the build directory after a successful build
    
    Capturing the build directory takes time and uses disk space of the CAS
    cache. If the `cache-buildtrees` option is set to `auto` (the default)
    or `never`, the contents of the build directory can be removed after a
    successful build as the build tree is no longer needed, avoiding the
    capture costs.
---
 src/buildstream/buildelement.py          |  5 ++++
 src/buildstream/element.py               |  5 ++--
 src/buildstream/sandbox/_sandboxreapi.py |  7 +++++
 src/buildstream/sandbox/sandbox.py       | 47 ++++++++++++++++++++++++++++++++
 4 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/src/buildstream/buildelement.py b/src/buildstream/buildelement.py
index f9dc36fae..51952e0ee 100644
--- a/src/buildstream/buildelement.py
+++ b/src/buildstream/buildelement.py
@@ -338,6 +338,11 @@ class BuildElement(Element):
                 for cmd in commands:
                     self.__run_command(sandbox, cmd)
 
+            if not self._get_workspace():
+                # Empty the build directory after successful build to avoid overhead of
+                # capturing the build directory (unless buildtree caching has been configured).
+                sandbox._clean_build_directory()
+
             # Return the payload, this is configurable but is generally
             # always the /buildstream-install directory
             return self.get_variable("install-root")
diff --git a/src/buildstream/element.py b/src/buildstream/element.py
index 0ca7f59b7..d73045d81 100644
--- a/src/buildstream/element.py
+++ b/src/buildstream/element.py
@@ -1651,9 +1651,8 @@ class Element(Plugin):
                 # download when it's not needed.
                 buildroot = self.get_variable("build-root")
                 cache_buildtrees = context.cache_buildtrees
-                if cache_buildtrees != _CacheBuildTrees.NEVER:
-                    always_cache_buildtrees = cache_buildtrees == _CacheBuildTrees.ALWAYS
-                    sandbox._set_build_directory(buildroot, always=always_cache_buildtrees)
+                always_cache_buildtrees = cache_buildtrees == _CacheBuildTrees.ALWAYS
+                sandbox._set_build_directory(buildroot, always=always_cache_buildtrees)
 
                 if not self.BST_RUN_COMMANDS:
                     # Element doesn't need to run any commands in the sandbox.
diff --git a/src/buildstream/sandbox/_sandboxreapi.py b/src/buildstream/sandbox/_sandboxreapi.py
index 53f8b97a5..dbf06ac67 100644
--- a/src/buildstream/sandbox/_sandboxreapi.py
+++ b/src/buildstream/sandbox/_sandboxreapi.py
@@ -278,3 +278,10 @@ class _SandboxREAPIBatch(_SandboxBatch):
 
     def create_empty_file(self, name):
         self.script += "touch -- {}\n".format(shlex.quote(name))
+
+    def clean_directory(self, name):
+        # Do not treat error during cleanup as a fatal build error
+        self.script += "rm -rf -- {} || true\n".format(shlex.quote(name))
+        # Working directory may be a subdirectory of the build directory.
+        # Recreate it if necessary as output capture requires the working directory to exist.
+        self.script += "mkdir -p {} || true\n".format(shlex.quote(self.first_command.cwd))
diff --git a/src/buildstream/sandbox/sandbox.py b/src/buildstream/sandbox/sandbox.py
index d788899f6..400ab7bc3 100644
--- a/src/buildstream/sandbox/sandbox.py
+++ b/src/buildstream/sandbox/sandbox.py
@@ -526,6 +526,32 @@ class Sandbox:
             cwd_vdir = vdir.open_directory(cwd.lstrip(os.sep), create=True)
             cwd_vdir._create_empty_file(name)
 
+    # _clean_build_directory()
+    #
+    # Remove the contents of the build directory after a successful build to
+    # avoid the capture costs (CPU, I/O and buildbox-casd disk usage).
+    #
+    # This will be skipped if the `cache_buildtrees` configuration is set to `always`.
+    #
+    def _clean_build_directory(self):
+        assert self._build_directory
+
+        # Preserve build directory if it is to be stored in the artifact even for a successful build
+        if self._build_directory_always:
+            return
+
+        if self.__batch:
+            batch_clean = _SandboxBatchCleanDirectory(self._build_directory)
+
+            current_group = self.__batch.current_group
+            current_group.append(batch_clean)
+        else:
+            vdir = self.get_virtual_directory()
+            relative_path = self._build_directory.lstrip(os.sep)
+            if vdir.exists(relative_path):
+                vdir.remove(relative_path, recursive=True)
+                vdir.open_directory(relative_path, create=True)
+
     # _get_element_name()
     #
     # Get the plugin's element full name
@@ -642,6 +668,13 @@ class _SandboxBatch:
         cwd_vdir = vdir.open_directory(cwd.lstrip(os.sep), create=True)
         cwd_vdir._create_empty_file(name)
 
+    def clean_directory(self, name):
+        vdir = self.sandbox.get_virtual_directory()
+        relative_path = name.lstrip(os.sep)
+        if vdir.exists(relative_path):
+            vdir.remove(relative_path, recursive=True)
+            vdir.open_directory(relative_path, create=True)
+
 
 # _SandboxBatchItem()
 #
@@ -707,3 +740,17 @@ class _SandboxBatchFile(_SandboxBatchItem):
 
     def execute(self, batch):
         batch.create_empty_file(self.name)
+
+
+# _SandboxBatchCleanDirectory()
+#
+# A directory cleaning item in a command batch.
+#
+class _SandboxBatchCleanDirectory(_SandboxBatchItem):
+    def __init__(self, name):
+        super().__init__()
+
+        self.name = name
+
+    def execute(self, batch):
+        batch.clean_directory(self.name)