richardmaw-codethink pushed to branch richardmaw/shell-multi-stage at BuildStream / buildstream
Commits:
- 
b8aa5d7c
by Richard Maw at 2018-10-30T10:21:01Z
 - 
ca00aa8b
by Richard Maw at 2018-10-30T10:21:01Z
 - 
427dd289
by Richard Maw at 2018-10-30T10:21:01Z
 - 
5a7ccad0
by Richard Maw at 2018-10-30T10:21:01Z
 
6 changed files:
- NEWS
 - buildstream/_frontend/cli.py
 - + tests/integration/project/elements/shell/adds-bar.bst
 - + tests/integration/project/elements/shell/adds-foo.bst
 - tests/integration/project/elements/integration.bst → tests/integration/project/elements/shell/integration.bst
 - tests/integration/shell.py
 
Changes:
| 1 | 
+===============
 | 
|
| 2 | 
+buildstream 1.4
 | 
|
| 3 | 
+===============
 | 
|
| 4 | 
+  | 
|
| 5 | 
+  o `bst shell` learned the `-e` option for staging multiple elements
 | 
|
| 6 | 
+    provided the element's kind implements `BST_GRANULAR_STAGE`.
 | 
|
| 7 | 
+  | 
|
| 1 | 8 | 
 =================
 | 
| 2 | 9 | 
 buildstream 1.3.1
 | 
| 3 | 10 | 
 =================
 | 
| ... | ... | @@ -572,11 +572,13 @@ def show(app, elements, deps, except_, order, format_): | 
| 572 | 572 | 
               help="Mount a file or directory into the sandbox")
 | 
| 573 | 573 | 
 @click.option('--isolate', is_flag=True, default=False,
 | 
| 574 | 574 | 
               help='Create an isolated build sandbox')
 | 
| 575 | 
+@click.option('--element', '-e', 'elements', multiple=True,
 | 
|
| 576 | 
+              type=click.Path(readable=False), required=False)
 | 
|
| 575 | 577 | 
 @click.argument('element',
 | 
| 576 | 
-                type=click.Path(readable=False))
 | 
|
| 578 | 
+                type=click.Path(readable=False), required=False)
 | 
|
| 577 | 579 | 
 @click.argument('command', type=click.STRING, nargs=-1)
 | 
| 578 | 580 | 
 @click.pass_obj
 | 
| 579 | 
-def shell(app, element, sysroot, mount, isolate, build_, command):
 | 
|
| 581 | 
+def shell(app, element, elements, sysroot, mount, isolate, build_, command):
 | 
|
| 580 | 582 | 
     """Run a command in the target element's sandbox environment
 | 
| 581 | 583 | 
 | 
| 582 | 584 | 
     This will stage a temporary sysroot for running the target
 | 
| ... | ... | @@ -597,13 +599,21 @@ def shell(app, element, sysroot, mount, isolate, build_, command): | 
| 597 | 599 | 
     from .._project import HostMount
 | 
| 598 | 600 | 
     from .._pipeline import PipelineSelection
 | 
| 599 | 601 | 
 | 
| 602 | 
+    if elements and element is not None:
 | 
|
| 603 | 
+        command = [element] + list(command)
 | 
|
| 604 | 
+        element = None
 | 
|
| 605 | 
+    if not elements and element is not None:
 | 
|
| 606 | 
+        elements = [element]
 | 
|
| 607 | 
+    if not elements:
 | 
|
| 608 | 
+        raise AppError('No elemenets specified to open a shell in')
 | 
|
| 609 | 
+  | 
|
| 600 | 610 | 
     if build_:
 | 
| 601 | 611 | 
         scope = Scope.BUILD
 | 
| 602 | 612 | 
     else:
 | 
| 603 | 613 | 
         scope = Scope.RUN
 | 
| 604 | 614 | 
 | 
| 605 | 615 | 
     with app.initialized():
 | 
| 606 | 
-        dependencies = app.stream.load_selection((element,), selection=PipelineSelection.NONE)
 | 
|
| 616 | 
+        dependencies = app.stream.load_selection(elements, selection=PipelineSelection.NONE)
 | 
|
| 607 | 617 | 
         element = dependencies[0]
 | 
| 608 | 618 | 
         prompt = app.shell_prompt(element)
 | 
| 609 | 619 | 
         mounts = [
 | 
| ... | ... | @@ -611,7 +621,7 @@ def shell(app, element, sysroot, mount, isolate, build_, command): | 
| 611 | 621 | 
             for host_path, path in mount
 | 
| 612 | 622 | 
         ]
 | 
| 613 | 623 | 
         try:
 | 
| 614 | 
-            exitcode = app.stream.shell((element,), scope, prompt,
 | 
|
| 624 | 
+            exitcode = app.stream.shell(dependencies, scope, prompt,
 | 
|
| 615 | 625 | 
                                         directory=sysroot,
 | 
| 616 | 626 | 
                                         mounts=mounts,
 | 
| 617 | 627 | 
                                         isolate=isolate,
 | 
| 1 | 
+kind: manual
 | 
|
| 2 | 
+depends:
 | 
|
| 3 | 
+- base.bst
 | 
|
| 4 | 
+  | 
|
| 5 | 
+config:
 | 
|
| 6 | 
+  install-commands:
 | 
|
| 7 | 
+  - |
 | 
|
| 8 | 
+    install -D -m775 /proc/self/fd/0 %{install-root}%{bindir}/bar <<\EOF
 | 
|
| 9 | 
+    #!/bin/sh
 | 
|
| 10 | 
+    echo bar
 | 
|
| 11 | 
+    EOF
 | 
| 1 | 
+kind: manual
 | 
|
| 2 | 
+depends:
 | 
|
| 3 | 
+- base.bst
 | 
|
| 4 | 
+  | 
|
| 5 | 
+config:
 | 
|
| 6 | 
+  install-commands:
 | 
|
| 7 | 
+  - |
 | 
|
| 8 | 
+    install -D -m775 /proc/self/fd/0 %{install-root}%{bindir}/foo <<\EOF
 | 
|
| 9 | 
+    #!/bin/sh
 | 
|
| 10 | 
+    echo foo
 | 
|
| 11 | 
+    EOF
 | 
| ... | ... | @@ -29,9 +29,11 @@ DATA_DIR = os.path.join( | 
| 29 | 29 | 
 #    element (str): The element to build and run a shell with
 | 
| 30 | 30 | 
 #    isolate (bool): Whether to pass --isolate to `bst shell`
 | 
| 31 | 31 | 
 #
 | 
| 32 | 
-def execute_shell(cli, project, command, *, config=None, mount=None, element='base.bst', isolate=False):
 | 
|
| 32 | 
+def execute_shell(cli, project, command, *, config=None, mount=None, element='base.bst', elements=None, isolate=False):
 | 
|
| 33 | 33 | 
     # Ensure the element is built
 | 
| 34 | 
-    result = cli.run(project=project, project_config=config, args=['build', element])
 | 
|
| 34 | 
+    if elements is None:
 | 
|
| 35 | 
+        elements = [element]
 | 
|
| 36 | 
+    result = cli.run(project=project, project_config=config, args=['build'] + elements)
 | 
|
| 35 | 37 | 
     assert result.exit_code == 0
 | 
| 36 | 38 | 
 | 
| 37 | 39 | 
     args = ['shell']
 | 
| ... | ... | @@ -40,7 +42,7 @@ def execute_shell(cli, project, command, *, config=None, mount=None, element='ba | 
| 40 | 42 | 
     if mount is not None:
 | 
| 41 | 43 | 
         host_path, target_path = mount
 | 
| 42 | 44 | 
         args += ['--mount', host_path, target_path]
 | 
| 43 | 
-    args += [element, '--'] + command
 | 
|
| 45 | 
+    args += ["-e" + e for e in elements] + ['--'] + command
 | 
|
| 44 | 46 | 
 | 
| 45 | 47 | 
     return cli.run(project=project, project_config=config, args=args)
 | 
| 46 | 48 | 
 | 
| ... | ... | @@ -345,10 +347,51 @@ def test_sysroot_workspace_visible(cli, tmpdir, datafiles): | 
| 345 | 347 | 
 | 
| 346 | 348 | 
 | 
| 347 | 349 | 
 # Test system integration commands can access devices in /dev
 | 
| 350 | 
+@pytest.mark.integration
 | 
|
| 348 | 351 | 
 @pytest.mark.datafiles(DATA_DIR)
 | 
| 349 | 352 | 
 def test_integration_devices(cli, tmpdir, datafiles):
 | 
| 350 | 353 | 
     project = os.path.join(datafiles.dirname, datafiles.basename)
 | 
| 351 | 
-    element_name = 'integration.bst'
 | 
|
| 354 | 
+    element_name = 'shell/integration.bst'
 | 
|
| 352 | 355 | 
 | 
| 353 | 356 | 
     result = execute_shell(cli, project, ["true"], element=element_name)
 | 
| 354 | 357 | 
     assert result.exit_code == 0
 | 
| 358 | 
+  | 
|
| 359 | 
+  | 
|
| 360 | 
+# Test multiple element shell
 | 
|
| 361 | 
+@pytest.mark.integration
 | 
|
| 362 | 
+@pytest.mark.datafiles(DATA_DIR)
 | 
|
| 363 | 
+def test_shell_multiple_elements(cli, tmpdir, datafiles):
 | 
|
| 364 | 
+    project = os.path.join(datafiles.dirname, datafiles.basename)
 | 
|
| 365 | 
+  | 
|
| 366 | 
+    result = execute_shell(cli, project, ["sh", "-c", "foo && bar"],
 | 
|
| 367 | 
+                           elements=["shell/adds-foo.bst", "shell/adds-bar.bst"])
 | 
|
| 368 | 
+    assert result.exit_code == 0
 | 
|
| 369 | 
+  | 
|
| 370 | 
+  | 
|
| 371 | 
+# Test multiple element build shell
 | 
|
| 372 | 
+@pytest.mark.integration
 | 
|
| 373 | 
+@pytest.mark.datafiles(DATA_DIR)
 | 
|
| 374 | 
+def test_shell_multiple_workspace(cli, tmpdir, datafiles):
 | 
|
| 375 | 
+    project = os.path.join(datafiles.dirname, datafiles.basename)
 | 
|
| 376 | 
+    elements = {'workspace/workspace-mount.bst': os.path.join(cli.directory, 'workspace-mount'),
 | 
|
| 377 | 
+                'make/makehello.bst': os.path.join(cli.directory, 'makehello')}
 | 
|
| 378 | 
+  | 
|
| 379 | 
+    for element, workspace in elements.items():
 | 
|
| 380 | 
+        res = cli.run(project=project, args=['workspace', 'open', element, workspace])
 | 
|
| 381 | 
+        assert res.exit_code == 0
 | 
|
| 382 | 
+  | 
|
| 383 | 
+    for workspace in elements.values():
 | 
|
| 384 | 
+        with open(os.path.join(workspace, "workspace-exists"), "w") as f:
 | 
|
| 385 | 
+            pass
 | 
|
| 386 | 
+  | 
|
| 387 | 
+    # Ensure the dependencies of our build failing element are built
 | 
|
| 388 | 
+    result = cli.run(project=project, args=['build', 'base.bst'])
 | 
|
| 389 | 
+    assert result.exit_code == 0
 | 
|
| 390 | 
+  | 
|
| 391 | 
+    args = ['shell', '--build'] + ['-e' + e for e in elements]
 | 
|
| 392 | 
+    args += ['--', 'sh', '-c',
 | 
|
| 393 | 
+             'test -e /buildstream/test/workspace/workspace-mount.bst/workspace-exists && \
 | 
|
| 394 | 
+              test -e /buildstream/test/make/makehello.bst/workspace-exists']
 | 
|
| 395 | 
+    result = cli.run(project=project, args=args)
 | 
|
| 396 | 
+    assert result.exit_code == 0
 | 
|
| 397 | 
+    assert result.output == ''
 | 
