# Copyright (C) 2018 The Android Open Source Project # # 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 # # http://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. """Helper functions for updaters.""" import os import re import subprocess import sys from pathlib import Path from typing import List, Tuple, Type from base_updater import Updater # pylint: disable=import-error import metadata_pb2 # type: ignore def create_updater(metadata: metadata_pb2.MetaData, proj_path: Path, updaters: List[Type[Updater]]) -> Updater: """Creates corresponding updater object for a project. Args: metadata: Parsed proto for METADATA file. proj_path: Absolute path for the project. Returns: An updater object. Raises: ValueError: Occurred when there's no updater for all urls. """ for url in metadata.third_party.url: for updater_cls in updaters: updater = updater_cls(proj_path, url, metadata.third_party.version) if updater.is_supported_url(): return updater raise ValueError('No supported URL.') def replace_package(source_dir, target_dir) -> None: """Invokes a shell script to prepare and update a project. Args: source_dir: Path to the new downloaded and extracted package. target_dir: The path to the project in Android source tree. """ print('Updating {} using {}.'.format(target_dir, source_dir)) script_path = os.path.join(os.path.dirname(sys.argv[0]), 'update_package.sh') subprocess.check_call(['bash', script_path, source_dir, target_dir]) VERSION_SPLITTER_PATTERN: str = r'[\.\-_]' VERSION_PATTERN: str = (r'^(?P[^\d]*)' + r'(?P\d+(' + VERSION_SPLITTER_PATTERN + r'\d+)*)' + r'(?P.*)$') VERSION_RE: re.Pattern = re.compile(VERSION_PATTERN) VERSION_SPLITTER_RE: re.Pattern = re.compile(VERSION_SPLITTER_PATTERN) ParsedVersion = Tuple[List[int], str, str] def _parse_version(version: str) -> ParsedVersion: match = VERSION_RE.match(version) if match is None: raise ValueError('Invalid version.') try: prefix, version, suffix = match.group('prefix', 'version', 'suffix') versions = [int(v) for v in VERSION_SPLITTER_RE.split(version)] return (versions, str(prefix), str(suffix)) except IndexError: # pylint: disable=raise-missing-from raise ValueError('Invalid version.') def _match_and_get_version(old_ver: ParsedVersion, version: str) -> Tuple[bool, bool, List[int]]: try: new_ver = _parse_version(version) except ValueError: return (False, False, []) right_format = (new_ver[1:] == old_ver[1:]) right_length = len(new_ver[0]) == len(old_ver[0]) return (right_format, right_length, new_ver[0]) def get_latest_version(current_version: str, version_list: List[str]) -> str: """Gets the latest version name from a list of versions. The new version must have the same prefix and suffix with old version. If no matched version is newer, current version name will be returned. """ parsed_current_ver = _parse_version(current_version) latest = max( version_list, key=lambda ver: _match_and_get_version(parsed_current_ver, ver), default=None) if not latest: raise ValueError('No matching version.') return latest