• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2022 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Pip install Pigweed Python packages."""
15
16import argparse
17from pathlib import Path
18import subprocess
19import sys
20from typing import List, Tuple
21
22try:
23    from pw_build.python_package import load_packages
24except ImportError:
25    # Load from python_package from this directory if pw_build is not available.
26    from python_package import load_packages  # type: ignore
27
28
29def _parse_args() -> Tuple[argparse.Namespace, List[str]]:
30    parser = argparse.ArgumentParser(description=__doc__)
31    parser.add_argument(
32        '--python-dep-list-files',
33        type=Path,
34        required=True,
35        help=(
36            'Path to a text file containing the list of Python package '
37            'metadata json files.'
38        ),
39    )
40    parser.add_argument(
41        '--gn-packages',
42        required=True,
43        help=(
44            'Comma separated list of GN python package ' 'targets to install.'
45        ),
46    )
47    parser.add_argument(
48        '--editable-pip-install',
49        action='store_true',
50        help=(
51            'If true run the pip install command with the '
52            '\'--editable\' option.'
53        ),
54    )
55    return parser.parse_known_args()
56
57
58class NoMatchingGnPythonDependency(Exception):
59    """An error occurred while processing a Python dependency."""
60
61
62def main(
63    python_dep_list_files: Path,
64    editable_pip_install: bool,
65    gn_targets: List[str],
66    pip_args: List[str],
67) -> int:
68    """Find matching python packages to pip install."""
69    pip_target_dirs: List[str] = []
70
71    py_packages = load_packages([python_dep_list_files], ignore_missing=True)
72    for pkg in py_packages:
73        valid_target = [target in pkg.gn_target_name for target in gn_targets]
74        if not any(valid_target):
75            continue
76        top_level_source_dir = pkg.package_dir
77        pip_target_dirs.append(str(top_level_source_dir.parent.resolve()))
78
79    if not pip_target_dirs:
80        raise NoMatchingGnPythonDependency(
81            'No matching GN Python dependency found to install.\n'
82            'GN Targets to pip install:\n' + '\n'.join(gn_targets) + '\n\n'
83            'Declared Python Dependencies:\n'
84            + '\n'.join(pkg.gn_target_name for pkg in py_packages)
85            + '\n\n'
86        )
87
88    for target in pip_target_dirs:
89        command_args = [sys.executable, "-m", "pip"]
90        command_args += pip_args
91        if editable_pip_install:
92            command_args.append('--editable')
93        command_args.append(target)
94
95        process = subprocess.run(
96            command_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
97        )
98        pip_output = process.stdout.decode()
99        if process.returncode != 0:
100            print(pip_output)
101            return process.returncode
102    return 0
103
104
105if __name__ == '__main__':
106    # Parse this script's args and pass any remaining args to pip.
107    argparse_args, remaining_args_for_pip = _parse_args()
108
109    # Split the comma separated string and remove leading slashes.
110    gn_target_names = [
111        target.lstrip('/')
112        for target in argparse_args.gn_packages.split(',')
113        if target  # The last target may be an empty string.
114    ]
115
116    result = main(
117        python_dep_list_files=argparse_args.python_dep_list_files,
118        editable_pip_install=argparse_args.editable_pip_install,
119        gn_targets=gn_target_names,
120        pip_args=remaining_args_for_pip,
121    )
122    sys.exit(result)
123