[Notes] [Git][BuildStream/buildstream][willsalmon/shellBuildTrees] 2 commits: Basic options for shell --build to use buildtrees\n issue #740



Title: GitLab

Will Salmon pushed to branch willsalmon/shellBuildTrees at BuildStream / buildstream

Commits:

6 changed files:

Changes:

  • buildstream/_context.py
    ... ... @@ -114,6 +114,9 @@ class Context():
    114 114
             # Whether or not to attempt to pull build trees globally
    
    115 115
             self.pull_buildtrees = None
    
    116 116
     
    
    117
    +        # Wheather or not to use a build tree, currenlty only used in shell --build but seems reusable.
    
    118
    +        self.usebuildtree = 'ask'
    
    119
    +
    
    117 120
             # Boolean, whether to offer to create a project for the user, if we are
    
    118 121
             # invoked outside of a directory where we can resolve the project.
    
    119 122
             self.prompt_auto_init = None
    
    ... ... @@ -182,7 +185,7 @@ class Context():
    182 185
             _yaml.node_validate(defaults, [
    
    183 186
                 'sourcedir', 'builddir', 'artifactdir', 'logdir',
    
    184 187
                 'scheduler', 'artifacts', 'logging', 'projects',
    
    185
    -            'cache', 'prompt', 'workspacedir',
    
    188
    +            'cache', 'prompt', 'workspacedir', 'usebuildtree',
    
    186 189
             ])
    
    187 190
     
    
    188 191
             for directory in ['sourcedir', 'builddir', 'artifactdir', 'logdir', 'workspacedir']:
    

  • buildstream/_frontend/cli.py
    ... ... @@ -582,11 +582,13 @@ def show(app, elements, deps, except_, order, format_):
    582 582
                   help="Mount a file or directory into the sandbox")
    
    583 583
     @click.option('--isolate', is_flag=True, default=False,
    
    584 584
                   help='Create an isolated build sandbox')
    
    585
    +@click.option('--use-buildtree', '-t', type=click.Choice(['ask', 'if_available','True', 'False']), default=None,
    
    586
    +              help='Defaults to if_available unless over ridden by user configuration')
    
    585 587
     @click.argument('element',
    
    586 588
                     type=click.Path(readable=False))
    
    587 589
     @click.argument('command', type=click.STRING, nargs=-1)
    
    588 590
     @click.pass_obj
    
    589
    -def shell(app, element, sysroot, mount, isolate, build_, command):
    
    591
    +def shell(app, element, sysroot, mount, isolate, build_, use_buildtree, command):
    
    590 592
         """Run a command in the target element's sandbox environment
    
    591 593
     
    
    592 594
         This will stage a temporary sysroot for running the target
    
    ... ... @@ -613,6 +615,14 @@ def shell(app, element, sysroot, mount, isolate, build_, command):
    613 615
             scope = Scope.RUN
    
    614 616
     
    
    615 617
         with app.initialized():
    
    618
    +        if use_buildtree is None:
    
    619
    +            ## set to user default.
    
    620
    +            use_buildtree = app.context.usebuildtree 
    
    621
    +        elif use_buildtree == 'True':
    
    622
    +            use_buildtree = True
    
    623
    +        elif use_buildtree == 'False':
    
    624
    +            use_buildtree = False
    
    625
    +
    
    616 626
             dependencies = app.stream.load_selection((element,), selection=PipelineSelection.NONE)
    
    617 627
             element = dependencies[0]
    
    618 628
             prompt = app.shell_prompt(element)
    
    ... ... @@ -620,12 +630,27 @@ def shell(app, element, sysroot, mount, isolate, build_, command):
    620 630
                 HostMount(path, host_path)
    
    621 631
                 for host_path, path in mount
    
    622 632
             ]
    
    633
    +        ##Todo: at this point we should be able to tell if we have a buildtree
    
    634
    +        ## check if buildtree exists and fail early hear.    
    
    635
    +        
    
    636
    +        if not element._cached_buildtree():
    
    637
    +            if use_buildtree == True:
    
    638
    +                raise AppError("No buildtree when requested")
    
    639
    +        else:
    
    640
    +            if app.interactive and use_buildtree == 'ask':
    
    641
    +                if click.confirm('Do you want to use the cached buildtree?'):
    
    642
    +                    use_buildtree = True
    
    643
    +                else:
    
    644
    +                    use_buildtree = False
    
    645
    +
    
    646
    +        
    
    623 647
             try:
    
    624 648
                 exitcode = app.stream.shell(element, scope, prompt,
    
    625 649
                                             directory=sysroot,
    
    626 650
                                             mounts=mounts,
    
    627 651
                                             isolate=isolate,
    
    628
    -                                        command=command)
    
    652
    +                                        command=command,
    
    653
    +                                        usebuildtree=use_buildtree)
    
    629 654
             except BstError as e:
    
    630 655
                 raise AppError("Error launching shell: {}".format(e), detail=e.detail) from e
    
    631 656
     
    

  • buildstream/_stream.py
    ... ... @@ -132,7 +132,8 @@ class Stream():
    132 132
                   directory=None,
    
    133 133
                   mounts=None,
    
    134 134
                   isolate=False,
    
    135
    -              command=None):
    
    135
    +              command=None,
    
    136
    +              usebuildtree=None):
    
    136 137
     
    
    137 138
             # Assert we have everything we need built, unless the directory is specified
    
    138 139
             # in which case we just blindly trust the directory, using the element
    
    ... ... @@ -147,7 +148,8 @@ class Stream():
    147 148
                     raise StreamError("Elements need to be built or downloaded before staging a shell environment",
    
    148 149
                                       detail="\n".join(missing_deps))
    
    149 150
     
    
    150
    -        return element._shell(scope, directory, mounts=mounts, isolate=isolate, prompt=prompt, command=command)
    
    151
    +        return element._shell(scope, directory, mounts=mounts, isolate=isolate, prompt=prompt, command=command,
    
    152
    +                usebuildtree=usebuildtree)
    
    151 153
     
    
    152 154
         # build()
    
    153 155
         #
    

  • buildstream/data/userconfig.yaml
    ... ... @@ -41,6 +41,8 @@ cache:
    41 41
       # Whether to pull build trees when downloading element artifacts
    
    42 42
       pull-buildtrees: False
    
    43 43
     
    
    44
    +usebuildtree: ask
    
    45
    +
    
    44 46
     #
    
    45 47
     #    Scheduler
    
    46 48
     #
    

  • buildstream/element.py
    ... ... @@ -1339,11 +1339,12 @@ class Element(Plugin):
    1339 1339
         # is used to stage things by the `bst checkout` codepath
    
    1340 1340
         #
    
    1341 1341
         @contextmanager
    
    1342
    -    def _prepare_sandbox(self, scope, directory, shell=False, integrate=True):
    
    1342
    +    def _prepare_sandbox(self, scope, directory, shell=False, integrate=True, usebuildtree=None):
    
    1343 1343
             # bst shell and bst checkout require a local sandbox.
    
    1344 1344
             bare_directory = True if directory else False
    
    1345 1345
             with self.__sandbox(directory, config=self.__sandbox_config, allow_remote=False,
    
    1346 1346
                                 bare_directory=bare_directory) as sandbox:
    
    1347
    +            sandbox.usebuildtree = usebuildtree
    
    1347 1348
     
    
    1348 1349
                 # Configure always comes first, and we need it.
    
    1349 1350
                 self.__configure_sandbox(sandbox)
    
    ... ... @@ -1387,7 +1388,7 @@ class Element(Plugin):
    1387 1388
             # Stage all sources that need to be copied
    
    1388 1389
             sandbox_vroot = sandbox.get_virtual_directory()
    
    1389 1390
             host_vdirectory = sandbox_vroot.descend(directory.lstrip(os.sep).split(os.sep), create=True)
    
    1390
    -        self._stage_sources_at(host_vdirectory, mount_workspaces=mount_workspaces)
    
    1391
    +        self._stage_sources_at(host_vdirectory, mount_workspaces=mount_workspaces, usebuildtree=sandbox.usebuildtree)
    
    1391 1392
     
    
    1392 1393
         # _stage_sources_at():
    
    1393 1394
         #
    
    ... ... @@ -1397,7 +1398,7 @@ class Element(Plugin):
    1397 1398
         #     vdirectory (:class:`.storage.Directory`): A virtual directory object to stage sources into.
    
    1398 1399
         #     mount_workspaces (bool): mount workspaces if True, copy otherwise
    
    1399 1400
         #
    
    1400
    -    def _stage_sources_at(self, vdirectory, mount_workspaces=True):
    
    1401
    +    def _stage_sources_at(self, vdirectory, mount_workspaces=True, usebuildtree=None):
    
    1401 1402
             with self.timed_activity("Staging sources", silent_nested=True):
    
    1402 1403
     
    
    1403 1404
                 if not isinstance(vdirectory, Directory):
    
    ... ... @@ -1421,7 +1422,7 @@ class Element(Plugin):
    1421 1422
                                                      .format(workspace.get_absolute_path())):
    
    1422 1423
                                 workspace.stage(temp_staging_directory)
    
    1423 1424
                     # Check if we have a cached buildtree to use
    
    1424
    -                elif self._cached_buildtree():
    
    1425
    +                elif usebuildtree:
    
    1425 1426
                         artifact_base, _ = self.__extract()
    
    1426 1427
                         import_dir = os.path.join(artifact_base, 'buildtree')
    
    1427 1428
                     else:
    
    ... ... @@ -1855,9 +1856,10 @@ class Element(Plugin):
    1855 1856
         # Returns: Exit code
    
    1856 1857
         #
    
    1857 1858
         # If directory is not specified, one will be staged using scope
    
    1858
    -    def _shell(self, scope=None, directory=None, *, mounts=None, isolate=False, prompt=None, command=None):
    
    1859
    +    def _shell(self, scope=None, directory=None, *, mounts=None, isolate=False, prompt=None, command=None,
    
    1860
    +            usebuildtree=None):
    
    1859 1861
     
    
    1860
    -        with self._prepare_sandbox(scope, directory, shell=True) as sandbox:
    
    1862
    +        with self._prepare_sandbox(scope, directory, shell=True, usebuildtree=usebuildtree) as sandbox:
    
    1861 1863
                 environment = self.get_environment()
    
    1862 1864
                 environment = copy.copy(environment)
    
    1863 1865
                 flags = SandboxFlags.INTERACTIVE | SandboxFlags.ROOT_READ_ONLY
    
    ... ... @@ -2232,7 +2234,6 @@ class Element(Plugin):
    2232 2234
                                         specs=self.__remote_execution_specs,
    
    2233 2235
                                         bare_directory=bare_directory,
    
    2234 2236
                                         allow_real_directory=False)
    
    2235
    -            yield sandbox
    
    2236 2237
     
    
    2237 2238
             elif directory is not None and os.path.exists(directory):
    
    2238 2239
                 if allow_remote and self.__remote_execution_specs:
    
    ... ... @@ -2250,7 +2251,6 @@ class Element(Plugin):
    2250 2251
                                                   config=config,
    
    2251 2252
                                                   bare_directory=bare_directory,
    
    2252 2253
                                                   allow_real_directory=not self.BST_VIRTUAL_DIRECTORY)
    
    2253
    -            yield sandbox
    
    2254 2254
     
    
    2255 2255
             else:
    
    2256 2256
                 os.makedirs(context.builddir, exist_ok=True)
    
    ... ... @@ -2264,6 +2264,11 @@ class Element(Plugin):
    2264 2264
                 # Cleanup the build dir
    
    2265 2265
                 utils._force_rmtree(rootdir)
    
    2266 2266
     
    
    2267
    +            return
    
    2268
    +        sandbox.usebuildtree = None
    
    2269
    +        yield sandbox
    
    2270
    +
    
    2271
    +
    
    2267 2272
         def __compose_default_splits(self, defaults):
    
    2268 2273
             project = self._get_project()
    
    2269 2274
     
    

  • tests/integration/build-tree.py
    ... ... @@ -32,6 +32,40 @@ def test_buildtree_staged(cli_integration, tmpdir, datafiles):
    32 32
         res.assert_success()
    
    33 33
     
    
    34 34
     
    
    35
    +@pytest.mark.datafiles(DATA_DIR)
    
    36
    +@pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
    
    37
    +def test_buildtree_staged_forced_true(cli_integration, tmpdir, datafiles):
    
    38
    +    # i.e. tests that cached build trees are staged by `bst shell --build`
    
    39
    +    project = os.path.join(datafiles.dirname, datafiles.basename)
    
    40
    +    element_name = 'build-shell/buildtree.bst'
    
    41
    +
    
    42
    +    res = cli_integration.run(project=project, args=['build', element_name])
    
    43
    +    res.assert_success()
    
    44
    +
    
    45
    +    res = cli_integration.run(project=project, args=[
    
    46
    +        'shell', '--build', '--use-buildtree=True', element_name, '--', 'cat', 'test'
    
    47
    +    ])
    
    48
    +    res.assert_success()
    
    49
    +    assert 'Hi' in res.output
    
    50
    +
    
    51
    +
    
    52
    +@pytest.mark.datafiles(DATA_DIR)
    
    53
    +@pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
    
    54
    +def test_buildtree_staged_forced_false(cli_integration, tmpdir, datafiles):
    
    55
    +    # i.e. tests that cached build trees are staged by `bst shell --build`
    
    56
    +    project = os.path.join(datafiles.dirname, datafiles.basename)
    
    57
    +    element_name = 'build-shell/buildtree.bst'
    
    58
    +
    
    59
    +    res = cli_integration.run(project=project, args=['build', element_name])
    
    60
    +    res.assert_success()
    
    61
    +
    
    62
    +    res = cli_integration.run(project=project, args=[
    
    63
    +        'shell', '--build', '--use-buildtree=False', element_name, '--', 'cat', 'test'
    
    64
    +    ])
    
    65
    +    ##res.assert_success()
    
    66
    +    assert not 'Hi' in res.output
    
    67
    +
    
    68
    +
    
    35 69
     @pytest.mark.datafiles(DATA_DIR)
    
    36 70
     @pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
    
    37 71
     def test_buildtree_from_failure(cli_integration, tmpdir, datafiles):
    
    ... ... @@ -83,3 +117,51 @@ def test_buildtree_pulled(cli, tmpdir, datafiles):
    83 117
                 'shell', '--build', element_name, '--', 'grep', '-q', 'Hi', 'test'
    
    84 118
             ])
    
    85 119
             res.assert_success()
    
    120
    +
    
    121
    +
    
    122
    +
    
    123
    +# Check that build shells work when pulled from a remote cache
    
    124
    +# This is to roughly simulate remote execution
    
    125
    +@pytest.mark.datafiles(DATA_DIR)
    
    126
    +@pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
    
    127
    +def test_buildtree_options(cli, tmpdir, datafiles):
    
    128
    +    project = os.path.join(datafiles.dirname, datafiles.basename)
    
    129
    +    element_name = 'build-shell/buildtree.bst'
    
    130
    +
    
    131
    +    with create_artifact_share(os.path.join(str(tmpdir), 'artifactshare')) as share:
    
    132
    +        # Build the element to push it to cache
    
    133
    +        cli.configure({
    
    134
    +            'artifacts': {'url': share.repo, 'push': True}
    
    135
    +        })
    
    136
    +        result = cli.run(project=project, args=['build', element_name])
    
    137
    +        result.assert_success()
    
    138
    +        assert cli.get_element_state(project, element_name) == 'cached'
    
    139
    +
    
    140
    +        # Discard the cache
    
    141
    +        cli.configure({
    
    142
    +            'artifacts': {'url': share.repo, 'push': True},
    
    143
    +            'artifactdir': os.path.join(cli.directory, 'artifacts2')
    
    144
    +        })
    
    145
    +        assert cli.get_element_state(project, element_name) != 'cached'
    
    146
    +
    
    147
    +        # Pull from cache, ensuring cli options is set to pull the buildtree
    
    148
    +        result = cli.run(project=project, args=['pull', '--deps', 'all', element_name])
    
    149
    +        result.assert_success()
    
    150
    +
    
    151
    +        # Check it's using the cached build tree
    
    152
    +        res = cli.run(project=project, args=[
    
    153
    +            'shell', '--build', element_name, '--use_buildtree=False', '--', 'cat', 'test'
    
    154
    +        ])
    
    155
    +        #res.assert_success()
    
    156
    +        assert not 'Hi' in res.output
    
    157
    +        # Check it's using the cached build tree
    
    158
    +        res = cli.run(project=project, args=[
    
    159
    +            'shell', '--build', element_name, '--use_buildtree=True', '--', 'cat', 'test'
    
    160
    +        ])
    
    161
    +        assert not 'Hi' in res.output
    
    162
    +
    
    163
    +
    
    164
    +
    
    165
    +
    
    166
    +
    
    167
    +



  • [Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]