[Notes] [Git][BuildStream/buildstream][valentindavid/cargo_plugin] 5 commits: _frontend/app.py: Set correct element-path in interactive bst-init



Title: GitLab

Valentin David pushed to branch valentindavid/cargo_plugin at BuildStream / buildstream

Commits:

5 changed files:

Changes:

  • buildstream/_frontend/app.py
    ... ... @@ -305,7 +305,6 @@ class App():
    305 305
             directory = self._main_options['directory']
    
    306 306
             directory = os.path.abspath(directory)
    
    307 307
             project_path = os.path.join(directory, 'project.conf')
    
    308
    -        elements_path = os.path.join(directory, element_path)
    
    309 308
     
    
    310 309
             try:
    
    311 310
                 # Abort if the project.conf already exists, unless `--force` was specified in `bst init`
    
    ... ... @@ -335,6 +334,7 @@ class App():
    335 334
                     raise AppError("Error creating project directory {}: {}".format(directory, e)) from e
    
    336 335
     
    
    337 336
                 # Create the elements sub-directory if it doesnt exist
    
    337
    +            elements_path = os.path.join(directory, element_path)
    
    338 338
                 try:
    
    339 339
                     os.makedirs(elements_path, exist_ok=True)
    
    340 340
                 except IOError as e:
    

  • buildstream/plugins/sources/cargo.py
    1
    +#
    
    2
    +#  Copyright (C) 2018 Codethink Limited
    
    3
    +#
    
    4
    +#  This program is free software; you can redistribute it and/or
    
    5
    +#  modify it under the terms of the GNU Lesser General Public
    
    6
    +#  License as published by the Free Software Foundation; either
    
    7
    +#  version 2 of the License, or (at your option) any later version.
    
    8
    +#
    
    9
    +#  This library is distributed in the hope that it will be useful,
    
    10
    +#  but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    11
    +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    
    12
    +#  Lesser General Public License for more details.
    
    13
    +#
    
    14
    +#  You should have received a copy of the GNU Lesser General Public
    
    15
    +#  License along with this library. If not, see <http://www.gnu.org/licenses/>.
    
    16
    +#
    
    17
    +#  Authors:
    
    18
    +#        Valentin David  <valentin david codethink co uk>
    
    19
    +
    
    20
    +"""
    
    21
    +cargo - stage files from cargo manifest
    
    22
    +=======================================
    
    23
    +
    
    24
    +`cargo` downloads and stages cargo crates based on a `Cargo.toml`
    
    25
    +manifest provided by a previous source.
    
    26
    +
    
    27
    +`ref` will contain the `Cargo.lock` file. `bst track` should be used
    
    28
    +to set it.
    
    29
    +
    
    30
    +When `keep-lock` is true, tracking will store the current `Cargo.lock`
    
    31
    +provided by previous sources. in the `ref`. If `keep-lock` is false or
    
    32
    +absent, then `ref` will be created for the latest available crates.
    
    33
    +
    
    34
    +**Host dependencies:**
    
    35
    +
    
    36
    +  * cargo
    
    37
    +  * cargo-vendor (can be installed with `cargo install cargo-vendor`).
    
    38
    +
    
    39
    +**Usage:**
    
    40
    +
    
    41
    +.. code:: yaml
    
    42
    +
    
    43
    +   # Specify the cargo source kind
    
    44
    +   kind: cargo
    
    45
    +
    
    46
    +   # Optionally give the subdirectory where the `Cargo.toml` manifest
    
    47
    +   # can be found.
    
    48
    +   subdir: subproject
    
    49
    +
    
    50
    +   # Optionally disallow rewriting `Cargo.lock`. In this case tracking
    
    51
    +   # will just read the existing file. If not used, then tracking
    
    52
    +   # will create `Cargo.lock`.
    
    53
    +   keep-lock: true
    
    54
    +"""
    
    55
    +
    
    56
    +
    
    57
    +import hashlib
    
    58
    +import os
    
    59
    +import errno
    
    60
    +
    
    61
    +from buildstream import Consistency, Source, utils, SourceError
    
    62
    +
    
    63
    +
    
    64
    +class CargoSource(Source):
    
    65
    +    # pylint: disable=attribute-defined-outside-init
    
    66
    +
    
    67
    +    BST_REQUIRES_PREVIOUS_SOURCES_TRACK = True
    
    68
    +    BST_REQUIRES_PREVIOUS_SOURCES_FETCH = True
    
    69
    +
    
    70
    +    def configure(self, node):
    
    71
    +        self.node_validate(node, ['ref', 'subdir', 'keep-lock'] + Source.COMMON_CONFIG_KEYS)
    
    72
    +        self.ref = self.node_get_member(node, str, 'ref', None)
    
    73
    +        self.subdir = self.node_get_member(node, str, 'subdir', '.')
    
    74
    +        self.keeplock = self.node_get_member(node, bool, 'keep-lock', False)
    
    75
    +        self.extra_path = None
    
    76
    +
    
    77
    +    def preflight(self):
    
    78
    +        self.host_cargo = utils.get_host_tool('cargo')
    
    79
    +
    
    80
    +        try:
    
    81
    +            utils.get_host_tool('cargo-vendor')
    
    82
    +        except utils.ProgramNotFoundError:
    
    83
    +            cargo_home = os.environ.get('CARGO_HOME', os.path.expanduser('~/.cargo'))
    
    84
    +            self.extra_path = os.path.join(cargo_home, 'bin')
    
    85
    +
    
    86
    +        self.call([self.host_cargo, 'vendor', '-V'],
    
    87
    +                  env=self._environment(),
    
    88
    +                  fail='Cannot find "cargo vendor". Please install it with "cargo install cargo-vendor".')
    
    89
    +
    
    90
    +    def get_unique_key(self):
    
    91
    +        return [self.subdir, self.ref]
    
    92
    +
    
    93
    +    def get_ref(self):
    
    94
    +        return self.ref
    
    95
    +
    
    96
    +    def load_ref(self, node):
    
    97
    +        self.ref = self.node_get_member(node, str, 'ref', None)
    
    98
    +
    
    99
    +    def set_ref(self, ref, node):
    
    100
    +        node['ref'] = self.ref = ref
    
    101
    +
    
    102
    +    def _environment(self, *, set_home=False):
    
    103
    +        env = {}
    
    104
    +        env.update(os.environ)
    
    105
    +        if self.extra_path:
    
    106
    +            path = env.get('PATH', '').split(':')
    
    107
    +            path.append(self.extra_path)
    
    108
    +            env['PATH'] = ':'.join(path)
    
    109
    +        if set_home:
    
    110
    +            home = os.path.join(self.get_mirror_directory(), 'home')
    
    111
    +            os.makedirs(home, exist_ok=True)
    
    112
    +            env['CARGO_HOME'] = home
    
    113
    +        return env
    
    114
    +
    
    115
    +    def _get_manifest(self, directory):
    
    116
    +        projectdir = os.path.join(directory, self.subdir)
    
    117
    +        manifest = os.path.join(projectdir, 'Cargo.toml')
    
    118
    +        lockfile = os.path.join(projectdir, 'Cargo.lock')
    
    119
    +        return manifest, lockfile
    
    120
    +
    
    121
    +    def track(self, previous_sources_dir):
    
    122
    +        manifest, lockfile = self._get_manifest(previous_sources_dir)
    
    123
    +
    
    124
    +        if not self.keeplock:
    
    125
    +            self.call([self.host_cargo, 'generate-lockfile', '--manifest-path', manifest],
    
    126
    +                      env=self._environment(set_home=True),
    
    127
    +                      fail="Failed to track cargo packages")
    
    128
    +        try:
    
    129
    +            with open(lockfile, 'rb') as f:
    
    130
    +                lockcontent = f.read().decode('utf-8')
    
    131
    +        except OSError as e:
    
    132
    +            if self.keeplock and e.errno == errno.ENOENT:
    
    133
    +                raise SourceError("{}: Cannot find Cargo.lock".format(self))
    
    134
    +            else:
    
    135
    +                raise
    
    136
    +
    
    137
    +        return lockcontent
    
    138
    +
    
    139
    +    def _get_stamp(self):
    
    140
    +        h = hashlib.sha256()
    
    141
    +        h.update(self.get_ref().encode('utf-8'))
    
    142
    +        return os.path.join(self.get_mirror_directory(), 'stamps', h.hexdigest())
    
    143
    +
    
    144
    +    def get_consistency(self):
    
    145
    +        if not self.ref:
    
    146
    +            return Consistency.INCONSISTENT
    
    147
    +        if os.path.exists(self._get_stamp()):
    
    148
    +            return Consistency.CACHED
    
    149
    +        return Consistency.RESOLVED
    
    150
    +
    
    151
    +    def fetch(self, previous_sources_dir):
    
    152
    +        manifest, lockfile = self._get_manifest(previous_sources_dir)
    
    153
    +        if not self.keeplock:
    
    154
    +            with open(lockfile, 'wb') as f:
    
    155
    +                f.write(self.get_ref().encode('utf-8'))
    
    156
    +
    
    157
    +        self.call([self.host_cargo, 'fetch', '--manifest-path', manifest, '--locked'],
    
    158
    +                  env=self._environment(set_home=True),
    
    159
    +                  fail="Failed to fetch cargo packages")
    
    160
    +        stamp = self._get_stamp()
    
    161
    +        os.makedirs(os.path.dirname(stamp), exist_ok=True)
    
    162
    +        with open(stamp, 'w'):
    
    163
    +            pass
    
    164
    +
    
    165
    +    def stage(self, directory):
    
    166
    +        manifest, lockfile = self._get_manifest(directory)
    
    167
    +        if not self.keeplock:
    
    168
    +            with open(lockfile, 'wb') as f:
    
    169
    +                f.write(self.ref.encode('utf-8'))
    
    170
    +
    
    171
    +        config = os.path.join(os.path.dirname(manifest), '.cargo', 'config')
    
    172
    +        os.makedirs(os.path.dirname(config), exist_ok=True)
    
    173
    +
    
    174
    +        vendordir = os.path.join(directory, 'vendor')
    
    175
    +        relvendordir = os.path.relpath(vendordir, os.path.dirname(manifest))
    
    176
    +
    
    177
    +        with utils.save_file_atomic(config, 'wb') as f:
    
    178
    +            self.call([self.host_cargo, 'vendor', '--frozen', '--relative-path', relvendordir],
    
    179
    +                      env=self._environment(set_home=True),
    
    180
    +                      cwd=os.path.dirname(manifest),
    
    181
    +                      stdout=f,
    
    182
    +                      fail="Failed to stage cargo packages")
    
    183
    +
    
    184
    +
    
    185
    +def setup():
    
    186
    +    return CargoSource

  • doc/source/core_plugins.rst
    ... ... @@ -59,6 +59,7 @@ Sources
    59 59
        sources/patch
    
    60 60
        sources/deb
    
    61 61
        sources/pip
    
    62
    +   sources/cargo
    
    62 63
     
    
    63 64
     
    
    64 65
     External plugins
    

  • setup.py
    ... ... @@ -39,6 +39,7 @@ if sys.version_info[0] != REQUIRED_PYTHON_MAJOR or sys.version_info[1] < REQUIRE
    39 39
     try:
    
    40 40
         from setuptools import setup, find_packages, Command
    
    41 41
         from setuptools.command.easy_install import ScriptWriter
    
    42
    +    from setuptools.command.test import test as TestCommand
    
    42 43
     except ImportError:
    
    43 44
         print("BuildStream requires setuptools in order to build. Install it using"
    
    44 45
               " your package manager (usually python3-setuptools) or via pip (pip3"
    
    ... ... @@ -219,9 +220,48 @@ class BuildGRPC(Command):
    219 220
                             f.write(code)
    
    220 221
     
    
    221 222
     
    
    223
    +#####################################################
    
    224
    +#                   Pytest command                  #
    
    225
    +#####################################################
    
    226
    +class PyTest(TestCommand):
    
    227
    +    """Defines a pytest command class to run tests from setup.py"""
    
    228
    +
    
    229
    +    user_options = TestCommand.user_options + [
    
    230
    +        ("addopts=", None, "Arguments to pass to pytest"),
    
    231
    +        ('index-url=''build_grpc': BuildGRPC,
    
    264
    +        'pytest': PyTest,
    
    225 265
         }
    
    226 266
         cmdclass.update(versioneer.get_cmdclass())
    
    227 267
         return cmdclass
    
    ... ... @@ -305,6 +345,5 @@ setup(name='BuildStream',
    305 345
               'grpcio >= 1.10',
    
    306 346
           ],
    
    307 347
           entry_points=bst_install_entry_points,
    
    308
    -      setup_requires=['pytest-runner'],
    
    309 348
           tests_require=dev_requires,
    
    310 349
           zip_safe=False)

  • tests/frontend/init.py
    ... ... @@ -3,6 +3,7 @@ import pytest
    3 3
     from tests.testutils import cli
    
    4 4
     
    
    5 5
     from buildstream import _yaml
    
    6
    +from buildstream._frontend.app import App
    
    6 7
     from buildstream._exceptions import ErrorDomain, LoadErrorReason
    
    7 8
     from buildstream._versions import BST_FORMAT_VERSION
    
    8 9
     
    
    ... ... @@ -98,3 +99,34 @@ def test_bad_element_path(cli, tmpdir, element_path):
    98 99
             'init', '--project-name', 'foo', '--element-path', element_path
    
    99 100
         ])
    
    100 101
         result.assert_main_error(ErrorDomain.APP, 'invalid-element-path')
    
    102
    +
    
    103
    +
    
    104
    +@pytest.mark.parametrize("element_path", [('foo'), ('foo/bar')])
    
    105
    +def test_element_path_interactive(cli, tmp_path, monkeypatch, element_path):
    
    106
    +    project = tmp_path
    
    107
    +    project_conf_path = project.joinpath('project.conf')
    
    108
    +
    
    109
    +    class DummyInteractiveApp(App):
    
    110
    +        def __init__(self, *args, **kwargs):
    
    111
    +            super().__init__(*args, **kwargs)
    
    112
    +            self.interactive = True
    
    113
    +
    
    114
    +        @classmethod
    
    115
    +        def create(cls, *args, **kwargs):
    
    116
    +            return DummyInteractiveApp(*args, **kwargs)
    
    117
    +
    
    118
    +        def _init_project_interactive(self, *args, **kwargs):
    
    119
    +            return ('project_name', '0', element_path)
    
    120
    +
    
    121
    +    monkeypatch.setattr(App, 'create', DummyInteractiveApp.create)
    
    122
    +
    
    123
    +    result = cli.run(project=str(project), args=['init'])
    
    124
    +    result.assert_success()
    
    125
    +
    
    126
    +    full_element_path = project.joinpath(element_path)
    
    127
    +    assert full_element_path.exists()
    
    128
    +
    
    129
    +    project_conf = _yaml.load(str(project_conf_path))
    
    130
    +    assert project_conf['name'] == 'project_name'
    
    131
    +    assert project_conf['format-version'] == '0'
    
    132
    +    assert project_conf['element-path'] == element_path



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