xref: /aosp_15_r20/external/pigweed/pw_env_setup_zephyr/py/pw_env_setup_zephyr/zephyr.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1#!/usr/bin/env python
2# Copyright 2024 The Pigweed Authors
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may not
5# use this file except in compliance with the License. You may obtain a copy of
6# the License at
7#
8#     https://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations under
14# the License.
15"""
16Zephyr CLI
17
18The 'zephyr' subcommand for the Pigweed CLI provides functionality for
19integrating a Pigweed and Zephyr application.
20"""
21
22import argparse
23import os
24import re
25import subprocess
26from pathlib import Path
27import logging
28import yaml
29
30from pw_env_setup_zephyr.argparser import add_parser_arguments
31
32
33_LOG = logging.getLogger('pw_zephyr.zephyr')
34
35
36def get_parser() -> argparse.ArgumentParser:
37    """Get a parser associated with this command."""
38    parser = argparse.ArgumentParser(
39        description=__doc__,
40        formatter_class=argparse.RawDescriptionHelpFormatter,
41    )
42    parser = add_parser_arguments(parser)
43    return parser
44
45
46def maybe_append_remote(remotes: list, name: str, path: Path) -> bool:
47    """Try appending a remote entry if it exists and is valid."""
48    if not path.exists():
49        _LOG.debug("'%s' does not exist, can't add '%s'", str(path), name)
50        return False
51    if not path.is_dir():
52        _LOG.debug("'%s' is not a directory, can't add '%s'", str(path), name)
53        return False
54
55    result = subprocess.run(
56        ['git', 'remote', '-v'],
57        stdout=subprocess.PIPE,
58        cwd=path,
59    )
60    if result.returncode != 0:
61        _LOG.error("Failed to find remotes for '%s' in '%s'", name, str(path))
62        return False
63    output = result.stdout.decode('utf-8')
64    _LOG.debug("git remotes=\n%s", output)
65    url_match = re.match(
66        r'^\w+\s+([\w@\:\/\-\.]+)\s+\(fetch\).*$', output, re.MULTILINE
67    )
68    if not url_match:
69        _LOG.error(
70            "Failed to find (fetch) remote for '%s' in '%s'", name, str(path)
71        )
72        return False
73    remotes.append(
74        {
75            'name': name,
76            'url-base': url_match.group(1),
77        }
78    )
79    return True
80
81
82def append_project(projects: list, name: str, path: Path) -> None:
83    """Append a project entry to the list"""
84    result = subprocess.run(
85        ['git', 'log', '--pretty=format:"%H"', '-n', '1'],
86        stdout=subprocess.PIPE,
87        cwd=path,
88    )
89    if result.returncode != 0:
90        _LOG.error(
91            "Failed to find hash branch for '%s' in '%s'", name, str(path)
92        )
93        return
94    output: str = result.stdout.decode('utf-8').strip().strip('"')
95    _LOG.debug("git current hash is '%s'", output)
96    if output == '':
97        _LOG.error("'%s' has no commit hash", str(path))
98        return
99
100    projects.append(
101        {
102            'name': name,
103            'remote': name,
104            'revision': output,
105            'path': str(
106                path.relative_to(os.path.commonpath([path, os.getcwd()]))
107            ),
108            'import': name == 'zephyr',
109        }
110    )
111
112
113def generate_manifest() -> dict:
114    """Generate a dictionary that matches a West manifest yaml format."""
115    pw_root_dir = Path(os.environ['PW_ROOT'])
116    zephyr_root_dir = pw_root_dir / 'environment' / 'packages' / 'zephyr'
117    remotes: list = []
118    projects: list = []
119    if maybe_append_remote(remotes, 'pigweed', pw_root_dir):
120        append_project(projects, 'pigweed', pw_root_dir)
121    if maybe_append_remote(remotes, 'zephyr', zephyr_root_dir):
122        append_project(projects, 'zephyr', zephyr_root_dir)
123
124    return {
125        'manifest': {
126            'remotes': remotes,
127            'projects': projects,
128        },
129    }
130
131
132def print_manifest() -> None:
133    """Print the content of a West manifest for the current Pigweed checkout"""
134    _LOG.info(yaml.safe_dump(generate_manifest()))
135
136
137def main() -> None:
138    """Main entry point for the 'pw zephyr' command."""
139    parser = get_parser()
140    args = parser.parse_args()
141    _LOG.addHandler(logging.StreamHandler())
142    _LOG.propagate = False
143    if args.verbose:
144        _LOG.setLevel(logging.DEBUG)
145    else:
146        _LOG.setLevel(logging.INFO)
147
148    if args.zephyr_subcommand == 'manifest':
149        print_manifest()
150    else:
151        parser.print_usage()
152
153
154if __name__ == '__main__':
155    main()
156