manifest: add project 'userdata' key

Add a new optional 'userdata' key to projects.

The value will be parsed and stored in the userdata attribute of the
corresponding west.manifest.Project object.

Example manifest file:

  manifest:
    projects:
      - name: foo
      - name: bar
        userdata: a-string
      - name: baz
        userdata:
          key: value
    defaults:
      remote: r
    remotes:
      - name: r
        url-base: base

Example Python usage:

  manifest = west.manifest.Manifest.from_file()

  foo, bar, baz = manifest.get_projects(['foo', 'bar', 'baz'])

  print(foo.userdata) # None
  print(bar.userdata) # prints 'a-string'
  print(baz.userdata) # prints {'key': 'value'}

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Martí Bolívar 2021-08-31 18:07:16 -07:00 committed by Marti Bolivar
parent 129e3431ae
commit 591a0aad49
3 changed files with 35 additions and 4 deletions

View File

@ -126,6 +126,10 @@ mapping:
groups:
required: false
include: groups-schema
# Arbitrary project user data.
userdata:
required: false
type: any
# If present, a list of project groups to enable and disable. Prefix
# a group name with "-" to disable it; prefix with "+" to enable it.

View File

@ -645,6 +645,7 @@ class Project:
an empty list.
- ``submodules``: the project's submodules configuration; either
a list of Submodule objects, or a boolean.
- ``userdata``: the parsed 'userdata' field in the manifest, or None
'''
def __eq__(self, other):
@ -656,7 +657,8 @@ class Project:
f'clone_depth={self.clone_depth}, '
f'west_commands={self.west_commands}, '
f'topdir={repr(self.topdir)}, '
f'groups={self.groups})')
f'groups={repr(self.groups)}, '
f'userdata={self.userdata})')
def __str__(self):
path_repr = repr(self.abspath or self.path)
@ -670,7 +672,8 @@ class Project:
west_commands: Optional[WestCommandsType] = None,
topdir: Optional[PathType] = None,
remote_name: Optional[str] = None,
groups: Optional[GroupsType] = None):
groups: Optional[GroupsType] = None,
userdata: Optional[Any] = None):
'''Project constructor.
If *topdir* is ``None``, then absolute path attributes
@ -702,6 +705,7 @@ class Project:
self.topdir = os.fspath(topdir) if topdir else None
self.remote_name = remote_name or 'origin'
self.groups: GroupsType = groups or []
self.userdata: Any = userdata
@property
def path(self) -> str:
@ -2026,13 +2030,16 @@ class Manifest:
self._malformed(
f'project {name}: "groups" cannot be combined with "import"')
userdata = pd.get('userdata')
ret = Project(name, url, pd.get('revision', defaults.revision), path,
submodules=self._load_submodules(pd.get('submodules'),
f'project {name}'),
clone_depth=pd.get('clone-depth'),
west_commands=pd.get('west-commands'),
topdir=self.topdir, remote_name=remote,
groups=groups)
groups=groups,
userdata=userdata)
# Make sure the return Project's path does not escape the
# workspace. We can't use escapes_directory() as that

View File

@ -534,7 +534,7 @@ def test_project_repr():
west-commands: some-path/west-commands.yml
''')
assert repr(m.projects[1]) == \
'Project("zephyr", "https://foo.com", revision="r", path=\'zephyr\', clone_depth=None, west_commands=[\'some-path/west-commands.yml\'], topdir=None, groups=[])' # noqa: E501
'Project("zephyr", "https://foo.com", revision="r", path=\'zephyr\', clone_depth=None, west_commands=[\'some-path/west-commands.yml\'], topdir=None, groups=[], userdata=None)' # noqa: E501
def test_project_sha(tmpdir):
tmpdir = Path(os.fspath(tmpdir))
@ -548,6 +548,26 @@ def test_project_sha(tmpdir):
topdir=tmpdir.parent)
assert project.sha(project.revision) == expected_sha
def test_project_userdata(tmpdir):
m = M('''\
defaults:
remote: r
remotes:
- name: r
url-base: base
projects:
- name: foo
- name: bar
userdata: a-string
- name: baz
userdata:
key: value
''')
foo, bar, baz = m.get_projects(['foo', 'bar', 'baz'])
assert foo.userdata is None
assert bar.userdata == 'a-string'
assert baz.userdata == {'key': 'value'}
def test_no_projects():
# An empty projects list is allowed.