| # Copyright 2022 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """Pip install Pigweed Python packages.""" |
| |
| import argparse |
| from pathlib import Path |
| import subprocess |
| import sys |
| from typing import List, Tuple |
| |
| try: |
| from pw_build.python_package import load_packages |
| except ImportError: |
| # Load from python_package from this directory if pw_build is not available. |
| from python_package import load_packages # type: ignore |
| |
| |
| def _parse_args() -> Tuple[argparse.Namespace, List[str]]: |
| parser = argparse.ArgumentParser(description=__doc__) |
| parser.add_argument( |
| '--python-dep-list-files', |
| type=Path, |
| required=True, |
| help= |
| 'Path to a text file containing the list of Python package metadata ' |
| 'json files.', |
| ) |
| parser.add_argument('--gn-packages', |
| required=True, |
| help=('Comma separated list of GN python package ' |
| 'targets to install.')) |
| parser.add_argument('--editable-pip-install', |
| action='store_true', |
| help=('If true run the pip install command with the ' |
| '\'--editable\' option.')) |
| return parser.parse_known_args() |
| |
| |
| class NoMatchingGnPythonDependency(Exception): |
| """An error occurred while processing a Python dependency.""" |
| |
| |
| def main(python_dep_list_files: Path, editable_pip_install: bool, |
| gn_targets: List[str], pip_args: List[str]) -> int: |
| """Find matching python packages to pip install.""" |
| pip_target_dirs: List[str] = [] |
| |
| py_packages = load_packages([python_dep_list_files], ignore_missing=True) |
| for pkg in py_packages: |
| valid_target = [target in pkg.gn_target_name for target in gn_targets] |
| if not any(valid_target): |
| continue |
| top_level_source_dir = pkg.package_dir |
| pip_target_dirs.append(str(top_level_source_dir.parent.resolve())) |
| |
| if not pip_target_dirs: |
| raise NoMatchingGnPythonDependency( |
| 'No matching GN Python dependency found to install.\n' |
| 'GN Targets to pip install:\n' + '\n'.join(gn_targets) + '\n\n' |
| 'Declared Python Dependencies:\n' + |
| '\n'.join(pkg.gn_target_name for pkg in py_packages) + '\n\n') |
| |
| for target in pip_target_dirs: |
| command_args = [sys.executable, "-m", "pip"] |
| command_args += pip_args |
| if editable_pip_install: |
| command_args.append('--editable') |
| command_args.append(target) |
| |
| process = subprocess.run(command_args, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.STDOUT) |
| pip_output = process.stdout.decode() |
| if process.returncode != 0: |
| print(pip_output) |
| return process.returncode |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| # Parse this script's args and pass any remaining args to pip. |
| argparse_args, remaining_args_for_pip = _parse_args() |
| |
| # Split the comma separated string and remove leading slashes. |
| gn_target_names = [ |
| target.lstrip('/') for target in argparse_args.gn_packages.split(',') |
| if target # The last target may be an empty string. |
| ] |
| |
| result = main(python_dep_list_files=argparse_args.python_dep_list_files, |
| editable_pip_install=argparse_args.editable_pip_install, |
| gn_targets=gn_target_names, |
| pip_args=remaining_args_for_pip) |
| sys.exit(result) |