From 018e62885d91b7038c8e769b8d9a593d20d16567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=AD=20Bol=C3=ADvar?= Date: Wed, 18 Nov 2020 21:10:44 -0800 Subject: [PATCH] west list: teach it about project groups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 standing for a space character: foofoo-group-1,foo-group-2foo barpath-for-bar bazbaz-groupbaz 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 --- src/west/app/project.py | 81 ++++++++++++++++++++++++++++------------- tests/test_project.py | 53 +++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 25 deletions(-) diff --git a/src/west/app/project.py b/src/west/app/project.py index 5adaa5a..3964142 100644 --- a/src/west/app/project.py +++ b/src/west/app/project.py @@ -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. diff --git a/tests/test_project.py b/tests/test_project.py index 74626e7..1989fd4 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -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()