summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Maw <jonathan.maw@codethink.co.uk>2018-11-19 17:26:59 +0000
committerJonathan Maw <jonathan.maw@codethink.co.uk>2018-12-11 16:15:19 +0000
commita1dee91ec00cc51b21acc1274887225325ea7baa (patch)
tree372ed18724be120f11f7c5cdf71f7874f8477d86
parent7cf8334380a3454842b4263112a54c5cca4765f5 (diff)
downloadbuildstream-a1dee91ec00cc51b21acc1274887225325ea7baa.tar.gz
Make specifying elements optional in bst commands
Known issues: * `bst shell` works, but `bst shell COMMANDS...` doesn't, because click has no way of separating optional args from variable-length args. * `bst checkout` and `bst source-checkout`'s usage strings mark LOCATION as an optional argument. Because click gets confused if there's an optional argument before a mandatory argument, I had to mark LOCATION as optional internally. * `bst workspace open` makes no sense with element being optional, so I skipped it. * `bst workspace close` will probably need to be revisited when multiple projects can own one workspace. * `bst workspace reset` will happily delete the directory you're currently in, requiring you to `cd $PWD` to see the contents of your directory. I could exclude the top-level directory of the workspace being deleted, but it is entirely valid to run workspace commands from deeper in the workspace. This is a part of #222
-rw-r--r--buildstream/_context.py16
-rw-r--r--buildstream/_frontend/cli.py98
2 files changed, 100 insertions, 14 deletions
diff --git a/buildstream/_context.py b/buildstream/_context.py
index 9a12b2b27..a3487a794 100644
--- a/buildstream/_context.py
+++ b/buildstream/_context.py
@@ -32,7 +32,7 @@ from ._message import Message, MessageType
from ._profile import Topics, profile_start, profile_end
from ._artifactcache import ArtifactCache
from ._artifactcache.cascache import CASCache
-from ._workspaces import Workspaces, WorkspaceProjectCache
+from ._workspaces import Workspaces, WorkspaceProjectCache, WORKSPACE_PROJECT_FILE
from .plugin import _plugin_lookup
@@ -649,6 +649,20 @@ class Context():
self._cascache = CASCache(self.artifactdir)
return self._cascache
+ # guess_element()
+ #
+ # Attempts to interpret which element the user intended to run commands on
+ #
+ # Returns:
+ # (str) The name of the element, or None if no element can be guessed
+ def guess_element(self):
+ workspace_project_dir, _ = utils._search_upward_for_files(self._directory, [WORKSPACE_PROJECT_FILE])
+ if workspace_project_dir:
+ workspace_project = self._workspace_project_cache.get(workspace_project_dir)
+ return workspace_project.get_default_element()
+ else:
+ return None
+
# _node_get_option_str()
#
diff --git a/buildstream/_frontend/cli.py b/buildstream/_frontend/cli.py
index e1fab38a0..4900b28a7 100644
--- a/buildstream/_frontend/cli.py
+++ b/buildstream/_frontend/cli.py
@@ -316,10 +316,15 @@ def build(app, elements, all_, track_, track_save, track_all, track_except, trac
if track_save:
click.echo("WARNING: --track-save is deprecated, saving is now unconditional", err=True)
- if track_all:
- track_ = elements
-
with app.initialized(session_name="Build"):
+ if not all_ and not elements:
+ guessed_target = app.context.guess_element()
+ if guessed_target:
+ elements = (guessed_target,)
+
+ if track_all:
+ track_ = elements
+
app.stream.build(elements,
track_targets=track_,
track_except=track_except,
@@ -371,6 +376,11 @@ def fetch(app, elements, deps, track_, except_, track_cross_junctions):
deps = PipelineSelection.ALL
with app.initialized(session_name="Fetch"):
+ if not elements:
+ guessed_target = app.context.guess_element()
+ if guessed_target:
+ elements = (guessed_target,)
+
app.stream.fetch(elements,
selection=deps,
except_targets=except_,
@@ -407,6 +417,11 @@ def track(app, elements, deps, except_, cross_junctions):
all: All dependencies of all specified elements
"""
with app.initialized(session_name="Track"):
+ if not elements:
+ guessed_target = app.context.guess_element()
+ if guessed_target:
+ elements = (guessed_target,)
+
# Substitute 'none' for 'redirect' so that element redirections
# will be done
if deps == 'none':
@@ -442,7 +457,13 @@ def pull(app, elements, deps, remote):
none: No dependencies, just the element itself
all: All dependencies
"""
+
with app.initialized(session_name="Pull"):
+ if not elements:
+ guessed_target = app.context.guess_element()
+ if guessed_target:
+ elements = (guessed_target,)
+
app.stream.pull(elements, selection=deps, remote=remote)
@@ -475,6 +496,11 @@ def push(app, elements, deps, remote):
all: All dependencies
"""
with app.initialized(session_name="Push"):
+ if not elements:
+ guessed_target = app.context.guess_element()
+ if guessed_target:
+ elements = (guessed_target,)
+
app.stream.push(elements, selection=deps, remote=remote)
@@ -545,6 +571,11 @@ def show(app, elements, deps, except_, order, format_):
$'---------- %{name} ----------\\n%{vars}'
"""
with app.initialized():
+ if not elements:
+ guessed_target = app.context.guess_element()
+ if guessed_target:
+ elements = (guessed_target,)
+
dependencies = app.stream.load_selection(elements,
selection=deps,
except_targets=except_)
@@ -573,7 +604,7 @@ def show(app, elements, deps, except_, order, format_):
help="Mount a file or directory into the sandbox")
@click.option('--isolate', is_flag=True, default=False,
help='Create an isolated build sandbox')
-@click.argument('element',
+@click.argument('element', required=False,
type=click.Path(readable=False))
@click.argument('command', type=click.STRING, nargs=-1)
@click.pass_obj
@@ -604,6 +635,11 @@ def shell(app, element, sysroot, mount, isolate, build_, command):
scope = Scope.RUN
with app.initialized():
+ if not element:
+ element = app.context.guess_element()
+ if not element:
+ raise AppError('Missing argument "ELEMENT".')
+
dependencies = app.stream.load_selection((element,), selection=PipelineSelection.NONE)
element = dependencies[0]
prompt = app.shell_prompt(element)
@@ -641,15 +677,24 @@ def shell(app, element, sysroot, mount, isolate, build_, command):
help="Create a tarball from the artifact contents instead "
"of a file tree. If LOCATION is '-', the tarball "
"will be dumped to the standard output.")
-@click.argument('element',
+@click.argument('element', required=False,
type=click.Path(readable=False))
-@click.argument('location', type=click.Path())
+@click.argument('location', type=click.Path(), required=False)
@click.pass_obj
def checkout(app, element, location, force, deps, integrate, hardlinks, tar):
"""Checkout a built artifact to the specified location
"""
from ..element import Scope
+ if not element and not location:
+ click.echo("ERROR: LOCATION is not specified", err=True)
+ sys.exit(-1)
+
+ if element and not location:
+ # Nasty hack to get around click's optional args
+ location = element
+ element = None
+
if hardlinks and tar:
click.echo("ERROR: options --hardlinks and --tar conflict", err=True)
sys.exit(-1)
@@ -662,6 +707,11 @@ def checkout(app, element, location, force, deps, integrate, hardlinks, tar):
scope = Scope.NONE
with app.initialized():
+ if not element:
+ element = app.context.guess_element()
+ if not element:
+ raise AppError('Missing argument "ELEMENT".')
+
app.stream.checkout(element,
location=location,
force=force,
@@ -683,14 +733,28 @@ def checkout(app, element, location, force, deps, integrate, hardlinks, tar):
help='The dependencies whose sources to checkout (default: none)')
@click.option('--fetch', 'fetch_', default=False, is_flag=True,
help='Fetch elements if they are not fetched')
-@click.argument('element',
+@click.argument('element', required=False,
type=click.Path(readable=False))
-@click.argument('location', type=click.Path())
+@click.argument('location', type=click.Path(), required=False)
@click.pass_obj
def source_checkout(app, element, location, deps, fetch_, except_):
"""Checkout sources of an element to the specified location
"""
+ if not element and not location:
+ click.echo("ERROR: LOCATION is not specified", err=True)
+ sys.exit(-1)
+
+ if element and not location:
+ # Nasty hack to get around click's optional args
+ location = element
+ element = None
+
with app.initialized():
+ if not element:
+ element = app.context.guess_element()
+ if not element:
+ raise AppError('Missing argument "ELEMENT".')
+
app.stream.source_checkout(element,
location=location,
deps=deps,
@@ -747,11 +811,15 @@ def workspace_open(app, no_checkout, force, track_, directory, elements):
def workspace_close(app, remove_dir, all_, elements):
"""Close a workspace"""
- if not (all_ or elements):
- click.echo('ERROR: no elements specified', err=True)
- sys.exit(-1)
-
with app.initialized():
+ if not (all_ or elements):
+ # NOTE: I may need to revisit this when implementing multiple projects
+ # opening one workspace.
+ element = app.context.guess_element()
+ if element:
+ elements = (element,)
+ else:
+ raise AppError('No elements specified')
# Early exit if we specified `all` and there are no workspaces
if all_ and not app.stream.workspace_exists():
@@ -808,7 +876,11 @@ def workspace_reset(app, soft, track_, all_, elements):
with app.initialized():
if not (all_ or elements):
- raise AppError('No elements specified to reset')
+ element = app.context.guess_element()
+ if element:
+ elements = (element,)
+ else:
+ raise AppError('No elements specified to reset')
if all_ and not app.stream.workspace_exists():
raise AppError("No open workspaces to reset")