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
