xref: /aosp_15_r20/tools/external_updater/updater_utils.py (revision 3c875a214f382db1236d28570d1304ce57138f32)
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