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

tvb pushed a commit to branch tristan/implement-retry-failed
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit b155a3d01236eea408c80a67f789801fea8bd1ff
Author: Tristan van Berkom <[email protected]>
AuthorDate: Mon Jul 10 22:29:22 2023 +0900

    Implement --retry-failed option to the bst build command.
    
    This patch includes:
    
      * A new --retry-failed option to the bst build command
    
      * A new configuration option in the "build" section of user config,
        defaulting to the current behavior of not retrying.
    
      * User configuration documentation update
    
      * Fix the completion test since we now complete a new CLI option
    
      * The element now drops the loaded failed build artifact at
        _load_artifact() time in the case that retrying failed builds
        was requested.
    
    This addresses #1335
---
 doc/source/using_config.rst          |  8 ++++++++
 src/buildstream/_context.py          |  6 +++++-
 src/buildstream/_frontend/cli.py     |  8 ++++++++
 src/buildstream/_stream.py           |  6 ++++++
 src/buildstream/data/userconfig.yaml |  5 +++++
 src/buildstream/element.py           | 18 +++++++++++++++++-
 tests/frontend/completions.py        |  2 ++
 7 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/doc/source/using_config.rst b/doc/source/using_config.rst
index b4659779a..19d10d582 100644
--- a/doc/source/using_config.rst
+++ b/doc/source/using_config.rst
@@ -332,6 +332,14 @@ Attributes
   available on the host and limit this with a hard coded value of ``8``, which 
was
   found to be an optimial number when building even on hosts with many cores.
 
+* ``retry-failed``
+
+  Try to build elements for which a failed build artifact is found when running
+  :ref:`bst build <invoking_build>`.
+
+  This is useful in the case that a build has failed due to insufficient 
resources
+  such as memory or disk space.
+
 * ``dependencies``
 
   This instructs what dependencies of the target elements should be built, 
valid
diff --git a/src/buildstream/_context.py b/src/buildstream/_context.py
index 676b686f8..a5df4304e 100644
--- a/src/buildstream/_context.py
+++ b/src/buildstream/_context.py
@@ -167,6 +167,9 @@ class Context:
         # Maximum jobs per build
         self.build_max_jobs: Optional[int] = None
 
+        # Retry any existing failed builds
+        self.build_retry_failed: Optional[bool] = None
+
         # Control which dependencies to build
         self.build_dependencies: Optional[_PipelineSelection] = None
 
@@ -427,8 +430,9 @@ class Context:
 
         # Load build config
         build = defaults.get_mapping("build")
-        build.validate_keys(["max-jobs", "dependencies"])
+        build.validate_keys(["max-jobs", "retry-failed", "dependencies"])
         self.build_max_jobs = build.get_int("max-jobs")
+        self.build_retry_failed = build.get_bool("retry-failed")
 
         dependencies = build.get_str("dependencies")
         if dependencies not in ["none", "all"]:
diff --git a/src/buildstream/_frontend/cli.py b/src/buildstream/_frontend/cli.py
index 468342c5f..8d8276a81 100644
--- a/src/buildstream/_frontend/cli.py
+++ b/src/buildstream/_frontend/cli.py
@@ -470,6 +470,12 @@ def init(app, project_name, min_version, element_path, 
force, target_directory):
 @click.option(
     "--ignore-project-source-remotes", is_flag=True, help="Ignore remote 
source cache servers recommended by projects"
 )
[email protected](
+    "--retry-failed",
+    "-r",
+    is_flag=True,
+    help="Try to build elements for which a failed build artifact is found",
+)
 @click.argument("elements", nargs=-1, type=click.Path(readable=False))
 @click.pass_obj
 def build(
@@ -480,6 +486,7 @@ def build(
     source_remotes,
     ignore_project_artifact_remotes,
     ignore_project_source_remotes,
+    retry_failed,
 ):
     """Build elements in a pipeline
 
@@ -519,6 +526,7 @@ def build(
             source_remotes=source_remotes,
             ignore_project_artifact_remotes=ignore_project_artifact_remotes,
             ignore_project_source_remotes=ignore_project_source_remotes,
+            retry_failed=retry_failed,
         )
 
 
diff --git a/src/buildstream/_stream.py b/src/buildstream/_stream.py
index 9372347e3..86ca04c63 100644
--- a/src/buildstream/_stream.py
+++ b/src/buildstream/_stream.py
@@ -369,6 +369,7 @@ class Stream:
     #    source_remotes: Source cache remotes specified on the commmand line
     #    ignore_project_artifact_remotes: Whether to ignore artifact remotes 
specified by projects
     #    ignore_project_source_remotes: Whether to ignore source remotes 
specified by projects
+    #    retry_failed: Try to build elements for which a failed build artifact 
is found
     #
     # If `remote` specified as None, then regular configuration will be used
     # to determine where to push artifacts to.
@@ -383,11 +384,16 @@ class Stream:
         source_remotes: Iterable[RemoteSpec] = (),
         ignore_project_artifact_remotes: bool = False,
         ignore_project_source_remotes: bool = False,
+        retry_failed: bool = False,
     ):
 
         # Flag the build state
         self._context.build = True
 
+        # Override user configuration if --retry-failed is specified
+        if retry_failed:
+            self._context.build_retry_failed = True
+
         elements = self._load(
             targets,
             selection=selection,
diff --git a/src/buildstream/data/userconfig.yaml 
b/src/buildstream/data/userconfig.yaml
index 1c30254da..7a5b98ad6 100644
--- a/src/buildstream/data/userconfig.yaml
+++ b/src/buildstream/data/userconfig.yaml
@@ -79,6 +79,11 @@ build:
   #
   max-jobs: 0
 
+  #
+  # Try to build elements for which a failed build artifact is found
+  #
+  retry-failed: False
+
   #
   # Control which dependencies to build
   #
diff --git a/src/buildstream/element.py b/src/buildstream/element.py
index 1a97308e6..28df02949 100644
--- a/src/buildstream/element.py
+++ b/src/buildstream/element.py
@@ -1857,7 +1857,23 @@ class Element(Plugin):
         # Attempt to pull artifact with the strict cache key
         pulled = pull and artifact.pull(pull_buildtrees=pull_buildtrees)
 
+        # Ignore failed build artifacts if a retry was requested
+        ignore_failed_artifact = context.build and context.build_retry_failed
+
         if artifact.cached() or strict:
+            if artifact.cached() and ignore_failed_artifact:
+                success, _, _ = artifact.load_build_result()
+                if not success:
+                    artifact = Artifact(
+                        self,
+                        context,
+                        strong_key=artifact.strong_key,
+                        strict_key=artifact.strict_key,
+                        weak_key=artifact.weak_key,
+                    )
+                    artifact._cached = False
+                    pulled = False
+
             self.__artifact = artifact
             return pulled
         elif self.__pull_pending:
@@ -1892,7 +1908,7 @@ class Element(Plugin):
                 # build in non-strict mode unless the failed artifact's strong 
key is
                 # equal to the resolved strict key.
                 #
-                if artifact.strong_key != self.__strict_cache_key:
+                if ignore_failed_artifact or artifact.strong_key != 
self.__strict_cache_key:
                     artifact = Artifact(
                         self,
                         context,
diff --git a/tests/frontend/completions.py b/tests/frontend/completions.py
index 2c4afa197..7054abc22 100644
--- a/tests/frontend/completions.py
+++ b/tests/frontend/completions.py
@@ -152,6 +152,8 @@ def test_commands(cli, cmd, word_idx, expected):
                 "--source-remote ",
                 "--ignore-project-artifact-remotes ",
                 "--ignore-project-source-remotes ",
+                "--retry-failed ",
+                "-r ",
             ],
         ),
         # Test the behavior of completing after an option that has a

Reply via email to