diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/buildstream/_frontend/app.py | 19 | ||||
-rw-r--r-- | src/buildstream/_frontend/cli.py | 107 | ||||
-rw-r--r-- | src/buildstream/_stream.py | 81 | ||||
-rw-r--r-- | src/buildstream/element.py | 13 |
4 files changed, 70 insertions, 150 deletions
diff --git a/src/buildstream/_frontend/app.py b/src/buildstream/_frontend/app.py index b25a421c3..59bc3513b 100644 --- a/src/buildstream/_frontend/app.py +++ b/src/buildstream/_frontend/app.py @@ -446,15 +446,16 @@ class App: # if they are available in the execution context. # # Args: - # element_name (str): The element's full name - # element_key (tuple): The element's display key + # element (Element): The element # # Returns: # (str): The formatted prompt to display in the shell # - def shell_prompt(self, element_name, element_key): + def shell_prompt(self, element): - _, key, dim = element_key + element_name = element._get_full_name() + + _, key, dim = element._get_display_key() if self.colors: prompt = ( @@ -703,10 +704,14 @@ class App: if choice == "shell": click.echo("\nDropping into an interactive shell in the failed build sandbox\n", err=True) try: - unique_id, element_key = element - prompt = self.shell_prompt(full_name, element_key) + unique_id, _ = element self.stream.shell( - None, _Scope.BUILD, prompt, isolate=True, usebuildtree="always", unique_id=unique_id + None, + _Scope.BUILD, + self.shell_prompt, + isolate=True, + usebuildtree="always", + unique_id=unique_id, ) except BstError as e: click.echo("Error while attempting to create interactive shell: {}".format(e), err=True) diff --git a/src/buildstream/_frontend/cli.py b/src/buildstream/_frontend/cli.py index 946024bb7..5e42bda68 100644 --- a/src/buildstream/_frontend/cli.py +++ b/src/buildstream/_frontend/cli.py @@ -578,12 +578,9 @@ def show(app, elements, deps, except_, order, format_): "--use-buildtree", "-t", "cli_buildtree", - type=click.Choice(["ask", "try", "always", "never"]), - default="ask", - show_default=True, + is_flag=True, help=( - "Stage a buildtree. If `always` is set, will always fail to " - "build if a buildtree is not available." + "Stage a buildtree. Will fail if a buildtree is not available." " --pull and pull-buildtrees configuration is needed " "if trying to query for remotely cached buildtrees." ), @@ -617,11 +614,11 @@ def shell(app, element, mount, isolate, build_, cli_buildtree, pull_, command): from ..element import _Scope from .._project import HostMount - scope = _Scope.BUILD if build_ else _Scope.RUN + # Buildtree can only be used with build shells + if cli_buildtree: + build_ = True - # We may need to fetch dependency artifacts if we're pulling the artifact - selection = _PipelineSelection.ALL if pull_ else _PipelineSelection.NONE - use_buildtree = None + scope = _Scope.BUILD if build_ else _Scope.RUN with app.initialized(): if not element: @@ -629,104 +626,18 @@ def shell(app, element, mount, isolate, build_, cli_buildtree, pull_, command): if not element: raise AppError('Missing argument "ELEMENT".') - elements = app.stream.load_selection((element,), selection=selection, use_artifact_config=True) - - # last one will be the element we want to stage, previous ones are - # elements to try and pull - element = elements[-1] - pull_dependencies = elements[:-1] if pull_ else None - - element_name = element._get_full_name() - element_key = element._get_display_key() - - prompt = app.shell_prompt(element_name, element_key) mounts = [HostMount(path, host_path) for host_path, path in mount] - artifact_is_cached = element._cached() - buildtree_is_cached = element._cached_buildtree() - buildtree_exists = element._buildtree_exists() - can_attempt_pull = app.context.pull_buildtrees and pull_ - - if cli_buildtree in ("always", "try"): - if buildtree_is_cached: - use_buildtree = cli_buildtree - # If element is already cached, we can check the proto to see if the buildtree existed - elif artifact_is_cached: - if not buildtree_exists: - if cli_buildtree == "always": - # Exit early if it won't be possible to even fetch a buildtree with always option - raise AppError("Artifact was created without buildtree, unable to launch shell with it") - click.echo( - "WARNING: Artifact created without buildtree, shell will be loaded without it", err=True - ) - elif can_attempt_pull: - use_buildtree = cli_buildtree - click.echo( - "WARNING: buildtree is not cached locally but did exist, will attempt to pull from available remotes", - err=True, - ) - else: - if cli_buildtree == "always": - # Exit early if it won't be possible to perform a fetch as pull semantics aren't present - raise AppError( - "Artifact has a buildtree but it isn't cached. Can be retried with --pull and pull-buildtrees configured" - ) - click.echo("WARNING: buildtree is not cached locally, shell will be loaded without it", err=True) - # If element isn't cached at all, we can't check the proto to see if it existed so can't exit early - elif can_attempt_pull: - use_buildtree = cli_buildtree - if use_buildtree == "always": - click.echo( - "WARNING: Element is not cached so buildtree status unknown, will attempt to pull from available remotes", - err=True, - ) - else: - if cli_buildtree == "always": - # Exit early as there is no buildtree locally & can_attempt_pull is False - raise AppError( - "Artifact not cached locally. Can be retried with --pull and pull-buildtrees configured" - ) - click.echo("WARNING: buildtree is not cached locally, shell will be loaded without it", err=True) - else: - # If the value has defaulted to ask and in non interactive mode, don't consider the buildtree, this - # being the default behaviour of the command - if app.interactive and cli_buildtree == "ask": - if buildtree_is_cached and bool(click.confirm("Do you want to use the cached buildtree?")): - use_buildtree = "always" - elif can_attempt_pull: - # If buildtree not cached, check if it's worth presenting the user a dialogue - message = None - if artifact_is_cached: - if buildtree_exists: - message = "Buildtree not cached but can be pulled if in available remotes, do you want to use it?" - else: - message = "Element is not cached so buildtree status unknown, do you want to pull and use it?" - if message: - try: - choice = click.prompt( - message, type=click.Choice(["try", "always", "never"]), err=True, show_choices=True, - ) - except click.Abort: - click.echo("Aborting", err=True) - sys.exit(-1) - - if choice != "never": - use_buildtree = choice - - # Raise warning if the element is cached in a failed state - if use_buildtree and element._cached_failure(): - click.echo("WARNING: using a buildtree from a failed build.", err=True) - try: exitcode = app.stream.shell( element, scope, - prompt, + app.shell_prompt, mounts=mounts, isolate=isolate, command=command, - usebuildtree=use_buildtree, - pull_dependencies=pull_dependencies, + usebuildtree=cli_buildtree, + pull_=pull_, ) except BstError as e: raise AppError("Error launching shell: {}".format(e), detail=e.detail) from e diff --git a/src/buildstream/_stream.py b/src/buildstream/_stream.py index 3b0a308e7..af0537fe1 100644 --- a/src/buildstream/_stream.py +++ b/src/buildstream/_stream.py @@ -167,14 +167,14 @@ class Stream: # Run a shell # # Args: - # element (Element): An Element object to run the shell for + # element (str): The name of the element to run the shell for # scope (_Scope): The scope for the shell (_Scope.BUILD or _Scope.RUN) - # prompt (str): The prompt to display in the shell + # prompt (function): A function to return the prompt to display in the shell # mounts (list of HostMount): Additional directories to mount into the sandbox # isolate (bool): Whether to isolate the environment like we do in builds # command (list): An argv to launch in the sandbox, or None - # usebuildtree (str): Whether to use a buildtree as the source, given cli option - # pull_dependencies ([Element]|None): Elements to attempt to pull + # usebuildtree (bool): Whether to use a buildtree as the source, given cli option + # pull_ (bool): Whether to attempt to pull missing or incomplete artifacts # unique_id: (str): Whether to use a unique_id to load an Element instance # # Returns: @@ -189,61 +189,62 @@ class Stream: mounts=None, isolate=False, command=None, - usebuildtree=None, - pull_dependencies=None, + usebuildtree=False, + pull_=False, unique_id=None ): # Load the Element via the unique_id if given if unique_id and element is None: element = Plugin._lookup(unique_id) + else: + selection = _PipelineSelection.BUILD if scope == _Scope.BUILD else _PipelineSelection.RUN + + elements = self.load_selection((element,), selection=selection, use_artifact_config=True) + + # Get element to stage from `targets` list. + # If scope is BUILD, it will not be in the `elements` list. + assert len(self.targets) == 1 + element = self.targets[0] + element._set_required(scope) + + if pull_: + self._scheduler.clear_queues() + self._add_queue(PullQueue(self._scheduler)) + plan = self._pipeline.add_elements([element], elements) + self._enqueue_plan(plan) + self._run() missing_deps = [dep for dep in self._pipeline.dependencies([element], scope) if not dep._cached()] if missing_deps: - if not pull_dependencies: - raise StreamError( - "Elements need to be built or downloaded before staging a shell environment", - detail="\n".join(list(map(lambda x: x._get_full_name(), missing_deps))), - ) - self._message(MessageType.INFO, "Attempting to fetch missing or incomplete artifacts") - self._scheduler.clear_queues() - self._add_queue(PullQueue(self._scheduler)) - plan = self._pipeline.add_elements([element], missing_deps) - self._enqueue_plan(plan) - self._run() + raise StreamError( + "Elements need to be built or downloaded before staging a shell environment", + detail="\n".join(list(map(lambda x: x._get_full_name(), missing_deps))), + ) - buildtree = False # Check if we require a pull queue attempt, with given artifact state and context if usebuildtree: if not element._cached_buildtree(): - require_buildtree = self._buildtree_pull_required([element]) - # Attempt a pull queue for the given element if remote and context allow it - if require_buildtree: - self._message(MessageType.INFO, "Attempting to fetch missing artifact buildtree") - self._scheduler.clear_queues() - self._add_queue(PullQueue(self._scheduler)) - self._enqueue_plan(require_buildtree) - self._run() - # Now check if the buildtree was successfully fetched - if element._cached_buildtree(): - buildtree = True - - if not buildtree: - message = "Buildtree is not cached locally or in available remotes" - if usebuildtree == "always": - raise StreamError(message) - - self._message(MessageType.INFO, message + ", shell will be loaded without it") - else: - buildtree = True + remotes_message = " or in available remotes" if pull_ else "" + if not element._cached(): + message = "Artifact not cached locally" + remotes_message + elif element._buildtree_exists(): + message = "Buildtree is not cached locally" + remotes_message + else: + message = "Artifact was created without buildtree" + raise StreamError(message) + + # Raise warning if the element is cached in a failed state + if element._cached_failure(): + self._message(MessageType.WARN, "using a buildtree from a failed build.") # Ensure we have our sources if we are launching a build shell - if scope == _Scope.BUILD and not buildtree: + if scope == _Scope.BUILD and not usebuildtree: self._fetch([element]) self._pipeline.assert_sources_cached([element]) return element._shell( - scope, mounts=mounts, isolate=isolate, prompt=prompt, command=command, usebuildtree=buildtree + scope, mounts=mounts, isolate=isolate, prompt=prompt(element), command=command, usebuildtree=usebuildtree ) # build() diff --git a/src/buildstream/element.py b/src/buildstream/element.py index 3df8894f0..c9480d4fc 100644 --- a/src/buildstream/element.py +++ b/src/buildstream/element.py @@ -1557,10 +1557,13 @@ class Element(Plugin): # _set_required(): # - # Mark this element and its runtime dependencies as required. + # Mark this element and its dependencies as required. # This unblocks pull/fetch/build. # - def _set_required(self): + # Args: + # scope (_Scope): The scope of dependencies to mark as required + # + def _set_required(self, scope=_Scope.RUN): assert utils._is_main_process(), "This has an impact on all elements and must be run in the main process" if self.__required: @@ -1569,9 +1572,9 @@ class Element(Plugin): self.__required = True - # Request artifacts of runtime dependencies - for dep in self._dependencies(_Scope.RUN, recurse=False): - dep._set_required() + # Request artifacts of dependencies + for dep in self._dependencies(scope, recurse=False): + dep._set_required(scope=_Scope.RUN) # When an element becomes required, it must be assembled for # the current pipeline. `__schedule_assembly_when_necessary()` |