1*3c875a21SAndroid Build Coastguard Worker# Copyright (C) 2018 The Android Open Source Project 2*3c875a21SAndroid Build Coastguard Worker# 3*3c875a21SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*3c875a21SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*3c875a21SAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*3c875a21SAndroid Build Coastguard Worker# 7*3c875a21SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*3c875a21SAndroid Build Coastguard Worker# 9*3c875a21SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*3c875a21SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*3c875a21SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*3c875a21SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*3c875a21SAndroid Build Coastguard Worker# limitations under the License. 14*3c875a21SAndroid Build Coastguard Worker"""Helper functions for updaters.""" 15*3c875a21SAndroid Build Coastguard Worker 16*3c875a21SAndroid Build Coastguard Workerfrom collections.abc import Sequence 17*3c875a21SAndroid Build Coastguard Workerimport os 18*3c875a21SAndroid Build Coastguard Workerimport re 19*3c875a21SAndroid Build Coastguard Workerimport subprocess 20*3c875a21SAndroid Build Coastguard Workerimport sys 21*3c875a21SAndroid Build Coastguard Workerfrom pathlib import Path 22*3c875a21SAndroid Build Coastguard Workerfrom typing import List, Tuple, Type 23*3c875a21SAndroid Build Coastguard Worker 24*3c875a21SAndroid Build Coastguard Workerfrom base_updater import Updater 25*3c875a21SAndroid Build Coastguard Workerimport fileutils 26*3c875a21SAndroid Build Coastguard Worker# pylint: disable=import-error 27*3c875a21SAndroid Build Coastguard Workerimport metadata_pb2 # type: ignore 28*3c875a21SAndroid Build Coastguard Worker 29*3c875a21SAndroid Build Coastguard Worker 30*3c875a21SAndroid Build Coastguard Workerdef create_updater(metadata: metadata_pb2.MetaData, proj_path: Path, 31*3c875a21SAndroid Build Coastguard Worker updaters: List[Type[Updater]]) -> Updater: 32*3c875a21SAndroid Build Coastguard Worker """Creates corresponding updater object for a project. 33*3c875a21SAndroid Build Coastguard Worker 34*3c875a21SAndroid Build Coastguard Worker Args: 35*3c875a21SAndroid Build Coastguard Worker metadata: Parsed proto for METADATA file. 36*3c875a21SAndroid Build Coastguard Worker proj_path: Absolute path for the project. 37*3c875a21SAndroid Build Coastguard Worker 38*3c875a21SAndroid Build Coastguard Worker Returns: 39*3c875a21SAndroid Build Coastguard Worker An updater object. 40*3c875a21SAndroid Build Coastguard Worker 41*3c875a21SAndroid Build Coastguard Worker Raises: 42*3c875a21SAndroid Build Coastguard Worker ValueError: Occurred when there's no updater for all urls. 43*3c875a21SAndroid Build Coastguard Worker """ 44*3c875a21SAndroid Build Coastguard Worker for identifier in metadata.third_party.identifier: 45*3c875a21SAndroid Build Coastguard Worker if identifier.type.lower() != 'homepage': 46*3c875a21SAndroid Build Coastguard Worker for updater_cls in updaters: 47*3c875a21SAndroid Build Coastguard Worker updater = updater_cls(proj_path, identifier, metadata.third_party.version) 48*3c875a21SAndroid Build Coastguard Worker if updater.is_supported_url(): 49*3c875a21SAndroid Build Coastguard Worker return updater 50*3c875a21SAndroid Build Coastguard Worker 51*3c875a21SAndroid Build Coastguard Worker raise ValueError('No supported URL.') 52*3c875a21SAndroid Build Coastguard Worker 53*3c875a21SAndroid Build Coastguard Worker 54*3c875a21SAndroid Build Coastguard Workerdef replace_package(source_dir, target_dir, temp_file=None) -> None: 55*3c875a21SAndroid Build Coastguard Worker """Invokes a shell script to prepare and update a project. 56*3c875a21SAndroid Build Coastguard Worker 57*3c875a21SAndroid Build Coastguard Worker Args: 58*3c875a21SAndroid Build Coastguard Worker source_dir: Path to the new downloaded and extracted package. 59*3c875a21SAndroid Build Coastguard Worker target_dir: The path to the project in Android source tree. 60*3c875a21SAndroid Build Coastguard Worker """ 61*3c875a21SAndroid Build Coastguard Worker 62*3c875a21SAndroid Build Coastguard Worker print(f'Updating {target_dir} using {source_dir}.') 63*3c875a21SAndroid Build Coastguard Worker script_path = os.path.join(os.path.dirname(sys.argv[0]), 64*3c875a21SAndroid Build Coastguard Worker 'update_package.sh') 65*3c875a21SAndroid Build Coastguard Worker subprocess.check_call(['bash', script_path, source_dir, target_dir, 66*3c875a21SAndroid Build Coastguard Worker "" if temp_file is None else temp_file]) 67*3c875a21SAndroid Build Coastguard Worker 68*3c875a21SAndroid Build Coastguard Worker 69*3c875a21SAndroid Build Coastguard Workerdef run_post_update(source_dir: Path, target_dir: Path) -> None: 70*3c875a21SAndroid Build Coastguard Worker """ 71*3c875a21SAndroid Build Coastguard Worker source_dir: Path to the new downloaded and extracted package. 72*3c875a21SAndroid Build Coastguard Worker target_dir: The path to the project in Android source tree. 73*3c875a21SAndroid Build Coastguard Worker """ 74*3c875a21SAndroid Build Coastguard Worker post_update_path = os.path.join(source_dir, 'post_update.sh') 75*3c875a21SAndroid Build Coastguard Worker if os.path.isfile(post_update_path): 76*3c875a21SAndroid Build Coastguard Worker print("Running post update script") 77*3c875a21SAndroid Build Coastguard Worker cmd: Sequence[str | Path] = ['bash', post_update_path, source_dir, target_dir] 78*3c875a21SAndroid Build Coastguard Worker print(f'Running {post_update_path}') 79*3c875a21SAndroid Build Coastguard Worker subprocess.check_call(cmd) 80*3c875a21SAndroid Build Coastguard Worker 81*3c875a21SAndroid Build Coastguard Worker 82*3c875a21SAndroid Build Coastguard WorkerVERSION_SPLITTER_PATTERN: str = r'[\.\-_]' 83*3c875a21SAndroid Build Coastguard WorkerVERSION_PATTERN: str = (r'^(?P<prefix>[^\d]*)' + r'(?P<version>\d+(' + 84*3c875a21SAndroid Build Coastguard Worker VERSION_SPLITTER_PATTERN + r'\d+)*)' + 85*3c875a21SAndroid Build Coastguard Worker r'(?P<suffix>.*)$') 86*3c875a21SAndroid Build Coastguard WorkerTAG_PATTERN: str = r".*refs/tags/(?P<tag>[^\^]*).*" 87*3c875a21SAndroid Build Coastguard WorkerTAG_RE: re.Pattern = re.compile(TAG_PATTERN) 88*3c875a21SAndroid Build Coastguard WorkerVERSION_RE: re.Pattern = re.compile(VERSION_PATTERN) 89*3c875a21SAndroid Build Coastguard WorkerVERSION_SPLITTER_RE: re.Pattern = re.compile(VERSION_SPLITTER_PATTERN) 90*3c875a21SAndroid Build Coastguard Worker 91*3c875a21SAndroid Build Coastguard WorkerParsedVersion = Tuple[List[int], str, str] 92*3c875a21SAndroid Build Coastguard Worker 93*3c875a21SAndroid Build Coastguard Worker 94*3c875a21SAndroid Build Coastguard Workerdef _parse_version(version: str) -> ParsedVersion: 95*3c875a21SAndroid Build Coastguard Worker match = VERSION_RE.match(version) 96*3c875a21SAndroid Build Coastguard Worker if match is None: 97*3c875a21SAndroid Build Coastguard Worker raise ValueError('Invalid version.') 98*3c875a21SAndroid Build Coastguard Worker try: 99*3c875a21SAndroid Build Coastguard Worker prefix, version, suffix = match.group('prefix', 'version', 'suffix') 100*3c875a21SAndroid Build Coastguard Worker versions = [int(v) for v in VERSION_SPLITTER_RE.split(version)] 101*3c875a21SAndroid Build Coastguard Worker return versions, str(prefix), str(suffix) 102*3c875a21SAndroid Build Coastguard Worker except IndexError: 103*3c875a21SAndroid Build Coastguard Worker # pylint: disable=raise-missing-from 104*3c875a21SAndroid Build Coastguard Worker raise ValueError('Invalid version.') 105*3c875a21SAndroid Build Coastguard Worker 106*3c875a21SAndroid Build Coastguard Worker 107*3c875a21SAndroid Build Coastguard Workerdef _match_and_get_version(old_ver: ParsedVersion, 108*3c875a21SAndroid Build Coastguard Worker version: str) -> Tuple[bool, bool, List[int]]: 109*3c875a21SAndroid Build Coastguard Worker try: 110*3c875a21SAndroid Build Coastguard Worker new_ver = _parse_version(version) 111*3c875a21SAndroid Build Coastguard Worker except ValueError: 112*3c875a21SAndroid Build Coastguard Worker return False, False, [] 113*3c875a21SAndroid Build Coastguard Worker 114*3c875a21SAndroid Build Coastguard Worker right_format = new_ver[1:] == old_ver[1:] 115*3c875a21SAndroid Build Coastguard Worker right_length = len(new_ver[0]) == len(old_ver[0]) 116*3c875a21SAndroid Build Coastguard Worker 117*3c875a21SAndroid Build Coastguard Worker return right_format, right_length, new_ver[0] 118*3c875a21SAndroid Build Coastguard Worker 119*3c875a21SAndroid Build Coastguard Worker 120*3c875a21SAndroid Build Coastguard Workerdef get_latest_stable_release_tag(current_version: str, version_list: List[str]) -> str: 121*3c875a21SAndroid Build Coastguard Worker """Gets the latest version name from a list of versions. 122*3c875a21SAndroid Build Coastguard Worker 123*3c875a21SAndroid Build Coastguard Worker The new version must have the same prefix and suffix with old version. 124*3c875a21SAndroid Build Coastguard Worker If no matched version is newer, current version name will be returned. 125*3c875a21SAndroid Build Coastguard Worker """ 126*3c875a21SAndroid Build Coastguard Worker parsed_current_ver = _parse_version(current_version) 127*3c875a21SAndroid Build Coastguard Worker 128*3c875a21SAndroid Build Coastguard Worker latest = max( 129*3c875a21SAndroid Build Coastguard Worker version_list, 130*3c875a21SAndroid Build Coastguard Worker key=lambda ver: _match_and_get_version(parsed_current_ver, ver), 131*3c875a21SAndroid Build Coastguard Worker default=None) 132*3c875a21SAndroid Build Coastguard Worker if not latest: 133*3c875a21SAndroid Build Coastguard Worker raise ValueError('No matching version.') 134*3c875a21SAndroid Build Coastguard Worker return latest 135*3c875a21SAndroid Build Coastguard Worker 136*3c875a21SAndroid Build Coastguard Worker 137*3c875a21SAndroid Build Coastguard Workerdef parse_remote_tag(line: str) -> str: 138*3c875a21SAndroid Build Coastguard Worker if (m := TAG_RE.match(line)) is not None: 139*3c875a21SAndroid Build Coastguard Worker return m.group("tag") 140*3c875a21SAndroid Build Coastguard Worker raise ValueError(f"Could not parse tag from {line}") 141*3c875a21SAndroid Build Coastguard Worker 142*3c875a21SAndroid Build Coastguard Worker 143*3c875a21SAndroid Build Coastguard Workerdef build(proj_path: Path) -> None: 144*3c875a21SAndroid Build Coastguard Worker tree = fileutils.find_tree_containing(proj_path) 145*3c875a21SAndroid Build Coastguard Worker cmd = [ 146*3c875a21SAndroid Build Coastguard Worker str(tree / 'build/soong/soong_ui.bash'), 147*3c875a21SAndroid Build Coastguard Worker "--build-mode", 148*3c875a21SAndroid Build Coastguard Worker "--modules-in-a-dir-no-deps", 149*3c875a21SAndroid Build Coastguard Worker f"--dir={str(proj_path)}", 150*3c875a21SAndroid Build Coastguard Worker ] 151*3c875a21SAndroid Build Coastguard Worker print('Building...') 152*3c875a21SAndroid Build Coastguard Worker subprocess.run(cmd, check=True, text=True) 153