west list: teach it about project groups

Add '-f {groups}' and '--all' options. Respect active projects by not
printing inactive ones by default.

This allows printing a project's groups, and hides non-updatable
groups by default.

Here we rely on the fact that groups may not have commas or whitespace
to ensure that the printed-out groups are a single whitespace-free and
comma separated string in the 'west list' output.

For example, in this manifest:

  manifest:
    # ...
    projects:
    - name: foo
      groups:
      - foo-group-1
      - foo-group-2
    - name: bar
      path: path-for-bar
    - name: baz
      groups:
      - baz-group

The output for "west list -f '{name} {groups} {path}'" is the following,
with <SPC> standing for a space character:

    foo<SPC>foo-group-1,foo-group-2<SPC>foo
    bar<SPC><SPC>path-for-bar
    baz<SPC>baz-group<SPC>baz

The empty string in the {groups} field for 'bar' can be picked up by
tools like cut(1) to find the entire list of groups. E.g., this command

    $ west list -f '{name} {groups} {path} | cut -d' ' -f 2

will print:

    foo-group-1,foo-group-2

    baz-group

Additionally, we repurpose the old --all command line option (that used
to print the location of the bootstrapped west repository back in the
0.5.x days) to print projects that would be skipped by 'west update'
by default due to groups or no-groups lists.

We're about to do something similar to several other commands, so
structure the help in the epilog in a way that we'll take advantage of
in subsequent patches.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Martí Bolívar 2020-11-18 21:10:44 -08:00 committed by Marti Bolivar
parent c1d9146d01
commit 018e62885d
2 changed files with 109 additions and 25 deletions

View File

@ -376,35 +376,38 @@ class List(_ProjectCommand):
default_fmt = '{name:12} {path:28} {revision:40} {url}'
parser = self._parser(
parser_adder,
epilog=textwrap.dedent(f'''\
FORMAT STRINGS
--------------
epilog=f'''\
{ACTIVE_PROJECTS_HELP}
Projects are listed using a Python 3 format string. Arguments
to the format string are accessed by name.
FORMAT STRINGS
--------------
The default format string is:
Projects are listed using a Python 3 format string. Arguments
to the format string are accessed by name.
"{default_fmt}"
The default format string is:
The following arguments are available:
"{default_fmt}"
- name: project name in the manifest
- url: full remote URL as specified by the manifest
- path: the relative path to the project from the top level,
as specified in the manifest where applicable
- abspath: absolute and normalized path to the project
- posixpath: like abspath, but in posix style, that is, with '/'
as the separator character instead of '\\'
- revision: project's revision as it appears in the manifest
- sha: project's revision as a SHA. Note that use of this requires
that the project has been cloned.
- cloned: "cloned" if the project has been cloned, "not-cloned"
otherwise
- clone_depth: project clone depth if specified, "None" otherwise
'''))
The following arguments are available:
- name: project name in the manifest
- url: full remote URL as specified by the manifest
- path: the relative path to the project from the top level,
as specified in the manifest where applicable
- abspath: absolute and normalized path to the project
- posixpath: like abspath, but in posix style, that is, with '/'
as the separator character instead of '\\'
- revision: project's revision as it appears in the manifest
- sha: project's revision as a SHA. Note that use of this requires
that the project has been cloned.
- cloned: "cloned" if the project has been cloned, "not-cloned"
otherwise
- clone_depth: project clone depth if specified, "None" otherwise
- groups: project groups, as a comma-separated list
''')
parser.add_argument('-a', '--all', action='store_true',
help='ignored for backwards compatibility'),
help='include inactive projects'),
parser.add_argument('--manifest-path-from-yaml', action='store_true',
help='''print the manifest repository's path
according to the manifest file YAML, which may
@ -414,7 +417,9 @@ class List(_ProjectCommand):
help='''format string to use to list each
project; see FORMAT STRINGS below.''')
self._add_projects_arg(parser)
parser.add_argument('projects', metavar='PROJECT', nargs='*',
help='''projects (by name or path) to operate on;
see ACTIVE PROJECTS below''')
return parser
@ -441,6 +446,13 @@ class List(_ProjectCommand):
self._setup_logging(args)
for project in self._projects(args.projects):
# Skip inactive projects unless the user said
# --all or named some projects explicitly.
if not (args.all or args.projects or
self.manifest.is_active(project)):
log.dbg(f'{project.name}: skipping, inactive')
continue
# Spelling out the format keys explicitly here gives us
# future-proofing if the internal Project representation
# ever changes.
@ -471,7 +483,8 @@ class List(_ProjectCommand):
revision=project.revision or 'N/A',
clone_depth=project.clone_depth or "None",
cloned=delay(cloned_thunk, project),
sha=delay(sha_thunk, project))
sha=delay(sha_thunk, project),
groups=','.join(project.groups))
except KeyError as e:
# The raised KeyError seems to just put the first
# invalid argument in the args tuple, regardless of
@ -1421,6 +1434,24 @@ MANIFEST_URL_DEFAULT = 'https://github.com/zephyrproject-rtos/zephyr'
# Default revision to check out of the manifest repository.
MANIFEST_REV_DEFAULT = 'master'
#
# Other shared globals.
#
ACTIVE_PROJECTS_HELP = '''\
ACTIVE PROJECTS
---------------
Default output is limited to "active" projects as determined by the:
- "groups" manifest file section
- "manifest.groups" local configuration option in .west/config
To include inactive projects as well, use "--all" or give an explicit
list of projects (by name or path). See the west documentation for
more details on active projects.
'''
#
# Helper class for creating format string keys that are expensive or
# undesirable to compute if not needed.

View File

@ -145,6 +145,59 @@ def test_list_manifest(west_update_tmpdir):
assert posixpath == Path(west_update_tmpdir).as_posix() + '/zephyr'
def test_list_groups(west_init_tmpdir):
with open('zephyr/west.yml', 'w') as f:
f.write("""
manifest:
defaults:
remote: r
remotes:
- name: r
url-base: https://example.com
projects:
- name: foo
groups:
- foo-group-1
- foo-group-2
- name: bar
path: path-for-bar
- name: baz
groups:
- baz-group
groups: [-foo-group-1,-foo-group-2,-baz-group]
""")
def check(command_string, expected):
out_lines = cmd(command_string).splitlines()
assert out_lines == expected
check('list -f "{name} .{groups}. {path}"',
['manifest .. zephyr',
'bar .. path-for-bar'])
check('list -f "{name} .{groups}. {path}" foo',
['foo .foo-group-1,foo-group-2. foo'])
check('list -f "{name} .{groups}. {path}" baz',
['baz .baz-group. baz'])
check('list -f "{name} .{groups}. {path}" foo bar baz',
['foo .foo-group-1,foo-group-2. foo',
'bar .. path-for-bar',
'baz .baz-group. baz'])
check('list --all -f "{name} .{groups}. {path}"',
['manifest .. zephyr',
'foo .foo-group-1,foo-group-2. foo',
'bar .. path-for-bar',
'baz .baz-group. baz'])
cmd('config manifest.groups foo-group-1')
check('list -f "{name} .{groups}. {path}"',
['manifest .. zephyr',
'foo .foo-group-1,foo-group-2. foo',
'bar .. path-for-bar'])
def test_manifest_freeze(west_update_tmpdir):
# We should be able to freeze manifests.
actual = cmd('manifest --freeze').splitlines()