1*6c119a46SAndroid Build Coastguard Worker#!/usr/bin/python3 2*6c119a46SAndroid Build Coastguard Worker# Copyright 2024 The Android Open Source Project 3*6c119a46SAndroid Build Coastguard Worker# 4*6c119a46SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*6c119a46SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*6c119a46SAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*6c119a46SAndroid Build Coastguard Worker# 8*6c119a46SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*6c119a46SAndroid Build Coastguard Worker# 10*6c119a46SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*6c119a46SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*6c119a46SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*6c119a46SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*6c119a46SAndroid Build Coastguard Worker# limitations under the License. 15*6c119a46SAndroid Build Coastguard Worker 16*6c119a46SAndroid Build Coastguard Workerimport argparse 17*6c119a46SAndroid Build Coastguard Workerimport datetime 18*6c119a46SAndroid Build Coastguard Workerimport logging 19*6c119a46SAndroid Build Coastguard Workerimport pathlib 20*6c119a46SAndroid Build Coastguard Workerimport re 21*6c119a46SAndroid Build Coastguard Workerimport shutil 22*6c119a46SAndroid Build Coastguard Workerimport subprocess 23*6c119a46SAndroid Build Coastguard Workerimport sys 24*6c119a46SAndroid Build Coastguard Worker 25*6c119a46SAndroid Build Coastguard WorkerDESCRIPTION = ( 26*6c119a46SAndroid Build Coastguard Worker 'Helper script for importing a snapshot from upstream Wayland protocol ' 27*6c119a46SAndroid Build Coastguard Worker 'sources.') 28*6c119a46SAndroid Build Coastguard Worker 29*6c119a46SAndroid Build Coastguard WorkerINTENDED_USAGE = (''' 30*6c119a46SAndroid Build Coastguard WorkerIntended Usage: 31*6c119a46SAndroid Build Coastguard Worker # Update the freedesktop.org subdirectory to version 1.32 32*6c119a46SAndroid Build Coastguard Worker # Check https://gitlab.freedesktop.org/wayland/wayland-protocols/-/tags 33*6c119a46SAndroid Build Coastguard Worker # for valid version tags. 34*6c119a46SAndroid Build Coastguard Worker ./import_snapshot.py freedesktop.org 1.32 35*6c119a46SAndroid Build Coastguard Worker 36*6c119a46SAndroid Build Coastguard Worker # Update the chromium.org subdirectory to the latest 37*6c119a46SAndroid Build Coastguard Worker ./import_snapshot.py chromium.org main 38*6c119a46SAndroid Build Coastguard Worker''') 39*6c119a46SAndroid Build Coastguard Worker 40*6c119a46SAndroid Build Coastguard Worker 41*6c119a46SAndroid Build Coastguard Workerclass GitRepo: 42*6c119a46SAndroid Build Coastguard Worker """Issues git commands against a local checkout located at some path.""" 43*6c119a46SAndroid Build Coastguard Worker 44*6c119a46SAndroid Build Coastguard Worker def __init__(self, base: pathlib.PurePath): 45*6c119a46SAndroid Build Coastguard Worker logging.debug("GitRepo base %s", base) 46*6c119a46SAndroid Build Coastguard Worker self._base = base 47*6c119a46SAndroid Build Coastguard Worker 48*6c119a46SAndroid Build Coastguard Worker @property 49*6c119a46SAndroid Build Coastguard Worker def base(self) -> pathlib.PurePath: 50*6c119a46SAndroid Build Coastguard Worker """Gets the base path used the repo.""" 51*6c119a46SAndroid Build Coastguard Worker return self._base 52*6c119a46SAndroid Build Coastguard Worker 53*6c119a46SAndroid Build Coastguard Worker def _git(self, 54*6c119a46SAndroid Build Coastguard Worker cmd: list[str], 55*6c119a46SAndroid Build Coastguard Worker capture_output: bool = True, 56*6c119a46SAndroid Build Coastguard Worker check: bool = True) -> subprocess.CompletedProcess: 57*6c119a46SAndroid Build Coastguard Worker return subprocess.run(['git', '-C', self._base] + cmd, 58*6c119a46SAndroid Build Coastguard Worker capture_output=capture_output, 59*6c119a46SAndroid Build Coastguard Worker check=check, 60*6c119a46SAndroid Build Coastguard Worker text=True) 61*6c119a46SAndroid Build Coastguard Worker 62*6c119a46SAndroid Build Coastguard Worker def get_hash_for_version(self, version) -> str: 63*6c119a46SAndroid Build Coastguard Worker """Gets the hash associated with a |version| tag or branch.""" 64*6c119a46SAndroid Build Coastguard Worker logging.debug("GitRepo.get_hash_for_version version %s", version) 65*6c119a46SAndroid Build Coastguard Worker return self._git(['show-ref', '--hash', 66*6c119a46SAndroid Build Coastguard Worker version]).stdout.splitlines()[0].strip() 67*6c119a46SAndroid Build Coastguard Worker 68*6c119a46SAndroid Build Coastguard Worker def git_ref_name_for_version(self, version) -> str | None: 69*6c119a46SAndroid Build Coastguard Worker """Gets the named ref corresponding to |version|, if one exists.""" 70*6c119a46SAndroid Build Coastguard Worker logging.debug("GitRepo.get_ref_name_for_version version %s", version) 71*6c119a46SAndroid Build Coastguard Worker ref = self._git(['describe', '--all', '--exact-match', version], 72*6c119a46SAndroid Build Coastguard Worker check=False).stdout.splitlines()[0].strip() 73*6c119a46SAndroid Build Coastguard Worker if ref.startswith('tags/'): 74*6c119a46SAndroid Build Coastguard Worker return ref.removeprefix('tags/') 75*6c119a46SAndroid Build Coastguard Worker if ref.startswith('heads/'): 76*6c119a46SAndroid Build Coastguard Worker return ref.removeprefix('heads/') 77*6c119a46SAndroid Build Coastguard Worker return None 78*6c119a46SAndroid Build Coastguard Worker 79*6c119a46SAndroid Build Coastguard Worker def get_files(self, version: str, 80*6c119a46SAndroid Build Coastguard Worker paths: list[pathlib.PurePath]) -> list[pathlib.Path]: 81*6c119a46SAndroid Build Coastguard Worker """Gets the list of files under |paths| that are part of the Git tree at |version|.""" 82*6c119a46SAndroid Build Coastguard Worker logging.debug("GitRepo.get_files version %s paths %s", version, paths) 83*6c119a46SAndroid Build Coastguard Worker stdout = self._git( 84*6c119a46SAndroid Build Coastguard Worker ['ls-tree', '-r', '--name-only', f'{version}^{{tree}}'] + 85*6c119a46SAndroid Build Coastguard Worker paths).stdout 86*6c119a46SAndroid Build Coastguard Worker return list(pathlib.PurePath(path) for path in stdout.splitlines()) 87*6c119a46SAndroid Build Coastguard Worker 88*6c119a46SAndroid Build Coastguard Worker def assert_no_uncommitted_changes(self) -> None: 89*6c119a46SAndroid Build Coastguard Worker """Asserts that the repo has no uncommited changes.""" 90*6c119a46SAndroid Build Coastguard Worker r = self._git(['diff-files', '--quiet', '--ignore-submodules'], 91*6c119a46SAndroid Build Coastguard Worker check=False) 92*6c119a46SAndroid Build Coastguard Worker if r.returncode: 93*6c119a46SAndroid Build Coastguard Worker sys.exit('Error: Your tree is dirty') 94*6c119a46SAndroid Build Coastguard Worker 95*6c119a46SAndroid Build Coastguard Worker r = self._git([ 96*6c119a46SAndroid Build Coastguard Worker 'diff-index', '--quiet', '--ignore-submodules', '--cached', 'HEAD' 97*6c119a46SAndroid Build Coastguard Worker ], 98*6c119a46SAndroid Build Coastguard Worker check=False) 99*6c119a46SAndroid Build Coastguard Worker if r.returncode: 100*6c119a46SAndroid Build Coastguard Worker sys.exit('Error: You have staged changes') 101*6c119a46SAndroid Build Coastguard Worker 102*6c119a46SAndroid Build Coastguard Worker def sparse_depth1_clone(self, 103*6c119a46SAndroid Build Coastguard Worker url: str, 104*6c119a46SAndroid Build Coastguard Worker version: str | None, 105*6c119a46SAndroid Build Coastguard Worker paths: list[str], 106*6c119a46SAndroid Build Coastguard Worker force_clean: bool = True) -> None: 107*6c119a46SAndroid Build Coastguard Worker """Performs a sparse clone with depth=1 of a repo. 108*6c119a46SAndroid Build Coastguard Worker 109*6c119a46SAndroid Build Coastguard Worker A sparse clone limits the clone to a particular set of files, and not 110*6c119a46SAndroid Build Coastguard Worker all the files available in the repo. 111*6c119a46SAndroid Build Coastguard Worker 112*6c119a46SAndroid Build Coastguard Worker A depth=1 clone fetches only the most recent version of each file 113*6c119a46SAndroid Build Coastguard Worker cloned, and not the entire history. 114*6c119a46SAndroid Build Coastguard Worker 115*6c119a46SAndroid Build Coastguard Worker Together that makes the checkout be faster and take up less space on 116*6c119a46SAndroid Build Coastguard Worker disk, which is important for large repositories like the Chromium 117*6c119a46SAndroid Build Coastguard Worker source tree. 118*6c119a46SAndroid Build Coastguard Worker 119*6c119a46SAndroid Build Coastguard Worker |url| gives the url to the remote repository to clone. 120*6c119a46SAndroid Build Coastguard Worker 121*6c119a46SAndroid Build Coastguard Worker |version| gives the version to clone. If not specified, 'HEAD' is assumed. 122*6c119a46SAndroid Build Coastguard Worker 123*6c119a46SAndroid Build Coastguard Worker Paths in |paths| are included in the sparse checkout, which also means 124*6c119a46SAndroid Build Coastguard Worker all files in the parents directories leading up to those directories are 125*6c119a46SAndroid Build Coastguard Worker included. if |paths| is an empty list, all files at the root of the 126*6c119a46SAndroid Build Coastguard Worker repository will be included. 127*6c119a46SAndroid Build Coastguard Worker 128*6c119a46SAndroid Build Coastguard Worker |force_clean| ensures any existing checkout at |base| is removed. 129*6c119a46SAndroid Build Coastguard Worker Setting this to False speeds up testing changes to the script when 130*6c119a46SAndroid Build Coastguard Worker syncing a particular version, as it will only be cloned the first 131*6c119a46SAndroid Build Coastguard Worker time. 132*6c119a46SAndroid Build Coastguard Worker """ 133*6c119a46SAndroid Build Coastguard Worker logging.debug( 134*6c119a46SAndroid Build Coastguard Worker "GitRepo.sparse_depth1_clone url %s version %s paths %s force_clean %s", 135*6c119a46SAndroid Build Coastguard Worker url, version, paths, force_clean) 136*6c119a46SAndroid Build Coastguard Worker self._base.parent.mkdir(parents=True, exist_ok=True) 137*6c119a46SAndroid Build Coastguard Worker if force_clean and self._base.exists(): 138*6c119a46SAndroid Build Coastguard Worker shutil.rmtree(self._base) 139*6c119a46SAndroid Build Coastguard Worker 140*6c119a46SAndroid Build Coastguard Worker if not self._base.exists(): 141*6c119a46SAndroid Build Coastguard Worker cmd = ['git', 'clone', '--filter=blob:none', '--depth=1'] 142*6c119a46SAndroid Build Coastguard Worker if paths: 143*6c119a46SAndroid Build Coastguard Worker cmd.extend(['--sparse']) 144*6c119a46SAndroid Build Coastguard Worker if version is not None and version != 'HEAD': 145*6c119a46SAndroid Build Coastguard Worker cmd.extend(['-b', version]) 146*6c119a46SAndroid Build Coastguard Worker cmd.extend([url, self._base]) 147*6c119a46SAndroid Build Coastguard Worker 148*6c119a46SAndroid Build Coastguard Worker subprocess.run(cmd, capture_output=False, check=True, text=True) 149*6c119a46SAndroid Build Coastguard Worker 150*6c119a46SAndroid Build Coastguard Worker if paths: 151*6c119a46SAndroid Build Coastguard Worker self._git(['sparse-checkout', 'add'] + paths) 152*6c119a46SAndroid Build Coastguard Worker 153*6c119a46SAndroid Build Coastguard Worker def add(self, path: pathlib.Path) -> None: 154*6c119a46SAndroid Build Coastguard Worker """Stages a local file |path| in the index.""" 155*6c119a46SAndroid Build Coastguard Worker logging.debug("GitRepo.add path %s", path) 156*6c119a46SAndroid Build Coastguard Worker self._git(['add', path]) 157*6c119a46SAndroid Build Coastguard Worker 158*6c119a46SAndroid Build Coastguard Worker def commit(self, 159*6c119a46SAndroid Build Coastguard Worker message: str, 160*6c119a46SAndroid Build Coastguard Worker allow_empty: bool = False, 161*6c119a46SAndroid Build Coastguard Worker auto_add: bool = True) -> None: 162*6c119a46SAndroid Build Coastguard Worker """Commits stages changed using |message|. 163*6c119a46SAndroid Build Coastguard Worker 164*6c119a46SAndroid Build Coastguard Worker If |allow_empty| is true, an empty commit is allowed. 165*6c119a46SAndroid Build Coastguard Worker If |auto_add| is true, changed files are added automatically. 166*6c119a46SAndroid Build Coastguard Worker """ 167*6c119a46SAndroid Build Coastguard Worker logging.debug("GitRepo.commit message %s allow_empty %s auto_add %s", 168*6c119a46SAndroid Build Coastguard Worker message, allow_empty, auto_add) 169*6c119a46SAndroid Build Coastguard Worker cmd = ['commit', '-m', message] 170*6c119a46SAndroid Build Coastguard Worker if allow_empty: 171*6c119a46SAndroid Build Coastguard Worker cmd.extend(['--allow-empty']) 172*6c119a46SAndroid Build Coastguard Worker if auto_add: 173*6c119a46SAndroid Build Coastguard Worker cmd.extend(['-a']) 174*6c119a46SAndroid Build Coastguard Worker 175*6c119a46SAndroid Build Coastguard Worker self._git(cmd, capture_output=False) 176*6c119a46SAndroid Build Coastguard Worker 177*6c119a46SAndroid Build Coastguard Worker 178*6c119a46SAndroid Build Coastguard Workerclass AndroidMetadata: 179*6c119a46SAndroid Build Coastguard Worker """Minimal set of functions for reading and updating METADATA files. 180*6c119a46SAndroid Build Coastguard Worker 181*6c119a46SAndroid Build Coastguard Worker Officially these files are meant to be read and written using code 182*6c119a46SAndroid Build Coastguard Worker generated from 183*6c119a46SAndroid Build Coastguard Worker //build/soong/compliance/project_metadata_proto/project_metadata.proto, 184*6c119a46SAndroid Build Coastguard Worker but using it would require adding a dependency on Python protocol buffer 185*6c119a46SAndroid Build Coastguard Worker libraries as well as the generated code for the .proto file. 186*6c119a46SAndroid Build Coastguard Worker 187*6c119a46SAndroid Build Coastguard Worker Instead we use the Python regex library module to parse and rewrite the 188*6c119a46SAndroid Build Coastguard Worker metadata, as we don't need to do anything really complicated. 189*6c119a46SAndroid Build Coastguard Worker """ 190*6c119a46SAndroid Build Coastguard Worker 191*6c119a46SAndroid Build Coastguard Worker def __init__(self, metadata_path: pathlib.Path): 192*6c119a46SAndroid Build Coastguard Worker assert metadata_path.exists() 193*6c119a46SAndroid Build Coastguard Worker self._metadata_path: pathlib.Path = metadata_path 194*6c119a46SAndroid Build Coastguard Worker self._content: str | None = None 195*6c119a46SAndroid Build Coastguard Worker self._url: str | None = None 196*6c119a46SAndroid Build Coastguard Worker self._paths: list[pathlib.PurePath] | None = None 197*6c119a46SAndroid Build Coastguard Worker 198*6c119a46SAndroid Build Coastguard Worker def _read_content(self) -> None: 199*6c119a46SAndroid Build Coastguard Worker if self._content is None: 200*6c119a46SAndroid Build Coastguard Worker with open(self._metadata_path, 'rt') as metadata_file: 201*6c119a46SAndroid Build Coastguard Worker self._content = metadata_file.read() 202*6c119a46SAndroid Build Coastguard Worker 203*6c119a46SAndroid Build Coastguard Worker def _write_content(self) -> None: 204*6c119a46SAndroid Build Coastguard Worker if self._content is not None: 205*6c119a46SAndroid Build Coastguard Worker with open(self._metadata_path, 'wt') as metadata_file: 206*6c119a46SAndroid Build Coastguard Worker metadata_file.write(self._content) 207*6c119a46SAndroid Build Coastguard Worker 208*6c119a46SAndroid Build Coastguard Worker def _read_raw_git_urls(self) -> None: 209*6c119a46SAndroid Build Coastguard Worker if self._url is None: 210*6c119a46SAndroid Build Coastguard Worker self._read_content() 211*6c119a46SAndroid Build Coastguard Worker 212*6c119a46SAndroid Build Coastguard Worker paths = [] 213*6c119a46SAndroid Build Coastguard Worker URL_PATTERN = r'url\s*{\s*type:\s*GIT\s*value:\s*"([^"]*)"\s*}' 214*6c119a46SAndroid Build Coastguard Worker for url in re.findall(URL_PATTERN, self._content): 215*6c119a46SAndroid Build Coastguard Worker base_url = url 216*6c119a46SAndroid Build Coastguard Worker path = None 217*6c119a46SAndroid Build Coastguard Worker 218*6c119a46SAndroid Build Coastguard Worker if '/-/tree/' in url: 219*6c119a46SAndroid Build Coastguard Worker base_url, path = url.split('/-/tree/') 220*6c119a46SAndroid Build Coastguard Worker _, path = path.split('/', 1) 221*6c119a46SAndroid Build Coastguard Worker elif '/+/' in url: 222*6c119a46SAndroid Build Coastguard Worker base_url, path = url.split('/+/') 223*6c119a46SAndroid Build Coastguard Worker _, path = path.split('/', 1) 224*6c119a46SAndroid Build Coastguard Worker 225*6c119a46SAndroid Build Coastguard Worker if self._url and self._url != base_url: 226*6c119a46SAndroid Build Coastguard Worker sys.exit( 227*6c119a46SAndroid Build Coastguard Worker f'Error: Inconsistent git URLs in {self._metadata_path} ({self._url} vs {base_url})' 228*6c119a46SAndroid Build Coastguard Worker ) 229*6c119a46SAndroid Build Coastguard Worker 230*6c119a46SAndroid Build Coastguard Worker self._url = base_url 231*6c119a46SAndroid Build Coastguard Worker if path: 232*6c119a46SAndroid Build Coastguard Worker paths.append(path) 233*6c119a46SAndroid Build Coastguard Worker 234*6c119a46SAndroid Build Coastguard Worker self._paths = tuple(paths) 235*6c119a46SAndroid Build Coastguard Worker 236*6c119a46SAndroid Build Coastguard Worker @property 237*6c119a46SAndroid Build Coastguard Worker def current_version(self) -> str: 238*6c119a46SAndroid Build Coastguard Worker """Obtains the current version according to the metadata.""" 239*6c119a46SAndroid Build Coastguard Worker self._read_content() 240*6c119a46SAndroid Build Coastguard Worker 241*6c119a46SAndroid Build Coastguard Worker match = re.search(r'version: "([^"]*)"', self._content) 242*6c119a46SAndroid Build Coastguard Worker if not match: 243*6c119a46SAndroid Build Coastguard Worker sys.exit( 244*6c119a46SAndroid Build Coastguard Worker f'Error: Unable to determine current version from {self._metadata_path}' 245*6c119a46SAndroid Build Coastguard Worker ) 246*6c119a46SAndroid Build Coastguard Worker return match.group(1) 247*6c119a46SAndroid Build Coastguard Worker 248*6c119a46SAndroid Build Coastguard Worker @property 249*6c119a46SAndroid Build Coastguard Worker def git_url(self) -> str: 250*6c119a46SAndroid Build Coastguard Worker """Obtains the git URL to use from the metadata.""" 251*6c119a46SAndroid Build Coastguard Worker self._read_raw_git_urls() 252*6c119a46SAndroid Build Coastguard Worker return self._url 253*6c119a46SAndroid Build Coastguard Worker 254*6c119a46SAndroid Build Coastguard Worker @property 255*6c119a46SAndroid Build Coastguard Worker def git_paths(self) -> list[pathlib.PurePath]: 256*6c119a46SAndroid Build Coastguard Worker """Obtains the child paths to sync from the metadata. 257*6c119a46SAndroid Build Coastguard Worker 258*6c119a46SAndroid Build Coastguard Worker This can be an empty list if the entire repo should be synced. 259*6c119a46SAndroid Build Coastguard Worker """ 260*6c119a46SAndroid Build Coastguard Worker self._read_raw_git_urls() 261*6c119a46SAndroid Build Coastguard Worker return list(self._paths) 262*6c119a46SAndroid Build Coastguard Worker 263*6c119a46SAndroid Build Coastguard Worker def update_version_and_import_date(self, version: str) -> None: 264*6c119a46SAndroid Build Coastguard Worker """Updates the version and import date in the metadata. 265*6c119a46SAndroid Build Coastguard Worker 266*6c119a46SAndroid Build Coastguard Worker |version| gives the version string to write. 267*6c119a46SAndroid Build Coastguard Worker The import date is set to the current date. 268*6c119a46SAndroid Build Coastguard Worker """ 269*6c119a46SAndroid Build Coastguard Worker self._read_content() 270*6c119a46SAndroid Build Coastguard Worker 271*6c119a46SAndroid Build Coastguard Worker now = datetime.datetime.now() 272*6c119a46SAndroid Build Coastguard Worker self._content = re.sub(r'version: "[^"]*"', f'version: "{version}"', 273*6c119a46SAndroid Build Coastguard Worker self._content) 274*6c119a46SAndroid Build Coastguard Worker self._content = re.sub( 275*6c119a46SAndroid Build Coastguard Worker r'last_upgrade_date {[^}]*}', 276*6c119a46SAndroid Build Coastguard Worker (f'last_upgrade_date {{ year: {now.year} month: {now.month} ' 277*6c119a46SAndroid Build Coastguard Worker f'day: {now.day} }}'), self._content) 278*6c119a46SAndroid Build Coastguard Worker 279*6c119a46SAndroid Build Coastguard Worker self._write_content() 280*6c119a46SAndroid Build Coastguard Worker 281*6c119a46SAndroid Build Coastguard Worker 282*6c119a46SAndroid Build Coastguard Workerdef must_ignore(path: pathlib.PurePath) -> bool: 283*6c119a46SAndroid Build Coastguard Worker """Checks if |path| should be ignored and not imported, as doing so might conflict with Android metadata..""" 284*6c119a46SAndroid Build Coastguard Worker IGNORE_PATTERNS: tuple[str] = ( 285*6c119a46SAndroid Build Coastguard Worker 'METADATA', 286*6c119a46SAndroid Build Coastguard Worker 'MODULE_LICENSE_*', 287*6c119a46SAndroid Build Coastguard Worker '**/OWNERS', 288*6c119a46SAndroid Build Coastguard Worker '**/Android.bp', 289*6c119a46SAndroid Build Coastguard Worker ) 290*6c119a46SAndroid Build Coastguard Worker ignore = any(path.match(pattern) for pattern in IGNORE_PATTERNS) 291*6c119a46SAndroid Build Coastguard Worker if ignore: 292*6c119a46SAndroid Build Coastguard Worker print('Ignoring source {path}') 293*6c119a46SAndroid Build Coastguard Worker return ignore 294*6c119a46SAndroid Build Coastguard Worker 295*6c119a46SAndroid Build Coastguard Worker 296*6c119a46SAndroid Build Coastguard Workerdef main(): 297*6c119a46SAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 298*6c119a46SAndroid Build Coastguard Worker description=DESCRIPTION, 299*6c119a46SAndroid Build Coastguard Worker epilog=INTENDED_USAGE, 300*6c119a46SAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter) 301*6c119a46SAndroid Build Coastguard Worker 302*6c119a46SAndroid Build Coastguard Worker parser.add_argument('group', 303*6c119a46SAndroid Build Coastguard Worker default=None, 304*6c119a46SAndroid Build Coastguard Worker help='The subdirectory (group) to update') 305*6c119a46SAndroid Build Coastguard Worker 306*6c119a46SAndroid Build Coastguard Worker parser.add_argument( 307*6c119a46SAndroid Build Coastguard Worker 'version', 308*6c119a46SAndroid Build Coastguard Worker nargs='?', 309*6c119a46SAndroid Build Coastguard Worker default='HEAD', 310*6c119a46SAndroid Build Coastguard Worker help='The official version to import. Uses HEAD by default.') 311*6c119a46SAndroid Build Coastguard Worker 312*6c119a46SAndroid Build Coastguard Worker parser.add_argument('--loglevel', 313*6c119a46SAndroid Build Coastguard Worker default='INFO', 314*6c119a46SAndroid Build Coastguard Worker choices=('DEBUG', 'INFO', 'WARNING', 'ERROR', 315*6c119a46SAndroid Build Coastguard Worker 'CRITICAL'), 316*6c119a46SAndroid Build Coastguard Worker help='Logging level.') 317*6c119a46SAndroid Build Coastguard Worker 318*6c119a46SAndroid Build Coastguard Worker parser.add_argument('--no-force-clean', 319*6c119a46SAndroid Build Coastguard Worker dest='force_clean', 320*6c119a46SAndroid Build Coastguard Worker default=True, 321*6c119a46SAndroid Build Coastguard Worker action='store_false', 322*6c119a46SAndroid Build Coastguard Worker help='Disables clean fetches of upstream code') 323*6c119a46SAndroid Build Coastguard Worker 324*6c119a46SAndroid Build Coastguard Worker parser.add_argument( 325*6c119a46SAndroid Build Coastguard Worker '--no-remove-old-files', 326*6c119a46SAndroid Build Coastguard Worker dest='remove_old_files', 327*6c119a46SAndroid Build Coastguard Worker default=True, 328*6c119a46SAndroid Build Coastguard Worker action='store_false', 329*6c119a46SAndroid Build Coastguard Worker help= 330*6c119a46SAndroid Build Coastguard Worker 'Disables syncing the previous version to determine what files to remove' 331*6c119a46SAndroid Build Coastguard Worker ) 332*6c119a46SAndroid Build Coastguard Worker 333*6c119a46SAndroid Build Coastguard Worker args: argparse.ArgumentParser = parser.parse_args() 334*6c119a46SAndroid Build Coastguard Worker 335*6c119a46SAndroid Build Coastguard Worker logging.basicConfig(level=getattr(logging, args.loglevel)) 336*6c119a46SAndroid Build Coastguard Worker 337*6c119a46SAndroid Build Coastguard Worker base = pathlib.Path(sys.argv[0]).parent.resolve().absolute() 338*6c119a46SAndroid Build Coastguard Worker assert base.exists() 339*6c119a46SAndroid Build Coastguard Worker 340*6c119a46SAndroid Build Coastguard Worker print( 341*6c119a46SAndroid Build Coastguard Worker f'Importing {args.group} Wayland protocols at {args.version} to {args.group}' 342*6c119a46SAndroid Build Coastguard Worker ) 343*6c119a46SAndroid Build Coastguard Worker 344*6c119a46SAndroid Build Coastguard Worker target_git = GitRepo(base) 345*6c119a46SAndroid Build Coastguard Worker target_git.assert_no_uncommitted_changes() 346*6c119a46SAndroid Build Coastguard Worker target_group_path = base / args.group 347*6c119a46SAndroid Build Coastguard Worker 348*6c119a46SAndroid Build Coastguard Worker meta = AndroidMetadata(target_group_path / 'METADATA') 349*6c119a46SAndroid Build Coastguard Worker 350*6c119a46SAndroid Build Coastguard Worker print(f'Cloning {meta.git_url} [sparse/limited] at {args.version}') 351*6c119a46SAndroid Build Coastguard Worker import_new_git = GitRepo(base / '.import' / args.group / (args.version)) 352*6c119a46SAndroid Build Coastguard Worker import_new_git.sparse_depth1_clone(meta.git_url, 353*6c119a46SAndroid Build Coastguard Worker args.version, 354*6c119a46SAndroid Build Coastguard Worker meta.git_paths, 355*6c119a46SAndroid Build Coastguard Worker force_clean=args.force_clean) 356*6c119a46SAndroid Build Coastguard Worker import_new_hash = import_new_git.get_hash_for_version(args.version) 357*6c119a46SAndroid Build Coastguard Worker import_new_ref_name = import_new_git.git_ref_name_for_version(args.version) 358*6c119a46SAndroid Build Coastguard Worker print(f'Synced "{import_new_hash} ({import_new_ref_name})"') 359*6c119a46SAndroid Build Coastguard Worker import_new_files = import_new_git.get_files(import_new_hash, 360*6c119a46SAndroid Build Coastguard Worker meta.git_paths) 361*6c119a46SAndroid Build Coastguard Worker if args.remove_old_files: 362*6c119a46SAndroid Build Coastguard Worker print( 363*6c119a46SAndroid Build Coastguard Worker f'Cloning {meta.git_url} [sparse/limited] at prior {meta.current_version}' 364*6c119a46SAndroid Build Coastguard Worker ) 365*6c119a46SAndroid Build Coastguard Worker import_old_git = GitRepo(base / '.import' / args.group / 366*6c119a46SAndroid Build Coastguard Worker meta.current_version) 367*6c119a46SAndroid Build Coastguard Worker import_old_git.sparse_depth1_clone(meta.git_url, 368*6c119a46SAndroid Build Coastguard Worker meta.current_version, 369*6c119a46SAndroid Build Coastguard Worker meta.git_paths, 370*6c119a46SAndroid Build Coastguard Worker force_clean=args.force_clean) 371*6c119a46SAndroid Build Coastguard Worker import_old_hash = import_old_git.get_hash_for_version( 372*6c119a46SAndroid Build Coastguard Worker meta.current_version) 373*6c119a46SAndroid Build Coastguard Worker print(f'Synced "{import_old_hash}"') 374*6c119a46SAndroid Build Coastguard Worker import_old_files = import_old_git.get_files(import_old_hash, 375*6c119a46SAndroid Build Coastguard Worker meta.git_paths) 376*6c119a46SAndroid Build Coastguard Worker 377*6c119a46SAndroid Build Coastguard Worker files_to_remove = set(import_old_files).difference(import_new_files) 378*6c119a46SAndroid Build Coastguard Worker for path in files_to_remove: 379*6c119a46SAndroid Build Coastguard Worker if must_ignore(path): 380*6c119a46SAndroid Build Coastguard Worker continue 381*6c119a46SAndroid Build Coastguard Worker old: pathlib.Path = target_group_path / path 382*6c119a46SAndroid Build Coastguard Worker logging.debug("removing old path %s", old) 383*6c119a46SAndroid Build Coastguard Worker old.unlink(missing_ok=True) 384*6c119a46SAndroid Build Coastguard Worker 385*6c119a46SAndroid Build Coastguard Worker for path in import_new_files: 386*6c119a46SAndroid Build Coastguard Worker if must_ignore(path): 387*6c119a46SAndroid Build Coastguard Worker continue 388*6c119a46SAndroid Build Coastguard Worker src: pathlib.Path = import_new_git.base / path 389*6c119a46SAndroid Build Coastguard Worker dst: pathlib.Path = target_group_path / path 390*6c119a46SAndroid Build Coastguard Worker logging.debug("copying %s to %s", src, dst) 391*6c119a46SAndroid Build Coastguard Worker dst.parent.mkdir(parents=True, exist_ok=True) 392*6c119a46SAndroid Build Coastguard Worker shutil.copy(src, dst) 393*6c119a46SAndroid Build Coastguard Worker target_git.add(target_group_path / path) 394*6c119a46SAndroid Build Coastguard Worker 395*6c119a46SAndroid Build Coastguard Worker meta.update_version_and_import_date(import_new_ref_name or import_new_hash) 396*6c119a46SAndroid Build Coastguard Worker target_git.add(target_group_path / 'METADATA') 397*6c119a46SAndroid Build Coastguard Worker 398*6c119a46SAndroid Build Coastguard Worker message = f''' 399*6c119a46SAndroid Build Coastguard WorkerUpdate to {args.group} protocols {import_new_ref_name or import_new_hash} 400*6c119a46SAndroid Build Coastguard Worker 401*6c119a46SAndroid Build Coastguard WorkerThis imports {import_new_hash} from the upstream repository. 402*6c119a46SAndroid Build Coastguard Worker 403*6c119a46SAndroid Build Coastguard WorkerTest: Builds 404*6c119a46SAndroid Build Coastguard Worker'''.lstrip() 405*6c119a46SAndroid Build Coastguard Worker target_git.commit(message, allow_empty=True) 406*6c119a46SAndroid Build Coastguard Worker 407*6c119a46SAndroid Build Coastguard Worker 408*6c119a46SAndroid Build Coastguard Workerif __name__ == '__main__': 409*6c119a46SAndroid Build Coastguard Worker main() 410