1*7594170eSAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project 2*7594170eSAndroid Build Coastguard Worker# 3*7594170eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*7594170eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*7594170eSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*7594170eSAndroid Build Coastguard Worker# 7*7594170eSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*7594170eSAndroid Build Coastguard Worker# 9*7594170eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*7594170eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*7594170eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*7594170eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*7594170eSAndroid Build Coastguard Worker# limitations under the License. 14*7594170eSAndroid Build Coastguard Workerimport argparse 15*7594170eSAndroid Build Coastguard Workerimport functools 16*7594170eSAndroid Build Coastguard Workerimport logging 17*7594170eSAndroid Build Coastguard Workerimport os 18*7594170eSAndroid Build Coastguard Workerimport re 19*7594170eSAndroid Build Coastguard Workerimport shutil 20*7594170eSAndroid Build Coastguard Workerimport uuid 21*7594170eSAndroid Build Coastguard Workerfrom pathlib import Path 22*7594170eSAndroid Build Coastguard Workerfrom string import Template 23*7594170eSAndroid Build Coastguard Workerfrom typing import Callable, Generator, Iterable 24*7594170eSAndroid Build Coastguard Workerfrom typing import NewType 25*7594170eSAndroid Build Coastguard Workerfrom typing import Optional 26*7594170eSAndroid Build Coastguard Workerfrom typing import TextIO 27*7594170eSAndroid Build Coastguard Worker 28*7594170eSAndroid Build Coastguard Workerimport cuj 29*7594170eSAndroid Build Coastguard Workerimport util 30*7594170eSAndroid Build Coastguard Workerfrom cuj import src 31*7594170eSAndroid Build Coastguard Workerfrom go_allowlists import GoAllowlistManipulator 32*7594170eSAndroid Build Coastguard Worker 33*7594170eSAndroid Build Coastguard Worker_ALLOWLISTS = "build/soong/android/allowlists/allowlists.go" 34*7594170eSAndroid Build Coastguard Worker 35*7594170eSAndroid Build Coastguard WorkerModuleType = NewType("ModuleType", str) 36*7594170eSAndroid Build Coastguard WorkerModuleName = NewType("ModuleName", str) 37*7594170eSAndroid Build Coastguard Worker 38*7594170eSAndroid Build Coastguard WorkerFilter = Callable[[ModuleType, ModuleName], bool] 39*7594170eSAndroid Build Coastguard Worker 40*7594170eSAndroid Build Coastguard Worker 41*7594170eSAndroid Build Coastguard Workerdef module_defs(src_lines: TextIO) -> Generator[tuple[ModuleType, str], None, None]: 42*7594170eSAndroid Build Coastguard Worker """ 43*7594170eSAndroid Build Coastguard Worker Split `scr_lines` (an Android.bp file) into module definitions and discard 44*7594170eSAndroid Build Coastguard Worker everything else (e.g. top level comments and assignments) 45*7594170eSAndroid Build Coastguard Worker Assumes that the Android.bp file is properly formatted, specifically, 46*7594170eSAndroid Build Coastguard Worker for any module definition: 47*7594170eSAndroid Build Coastguard Worker 1. the first line matches `start_pattern`, e.g. `cc_library {` 48*7594170eSAndroid Build Coastguard Worker 2. the last line matches a closing curly brace, i.e. '}' 49*7594170eSAndroid Build Coastguard Worker """ 50*7594170eSAndroid Build Coastguard Worker start_pattern = re.compile(r"^(?P<module_type>\w+)\s*\{\s*$") 51*7594170eSAndroid Build Coastguard Worker module_type: Optional[ModuleType] = None 52*7594170eSAndroid Build Coastguard Worker buffer = "" 53*7594170eSAndroid Build Coastguard Worker 54*7594170eSAndroid Build Coastguard Worker def in_module_def() -> bool: 55*7594170eSAndroid Build Coastguard Worker return buffer != "" 56*7594170eSAndroid Build Coastguard Worker 57*7594170eSAndroid Build Coastguard Worker for line in src_lines: 58*7594170eSAndroid Build Coastguard Worker # NB: line includes ending newline 59*7594170eSAndroid Build Coastguard Worker line = line.replace("$", "$$") # escape Templating meta-char '$' 60*7594170eSAndroid Build Coastguard Worker if not in_module_def(): 61*7594170eSAndroid Build Coastguard Worker m = start_pattern.match(line) 62*7594170eSAndroid Build Coastguard Worker if m: 63*7594170eSAndroid Build Coastguard Worker module_type = ModuleType(m.group("module_type")) 64*7594170eSAndroid Build Coastguard Worker buffer = line 65*7594170eSAndroid Build Coastguard Worker else: 66*7594170eSAndroid Build Coastguard Worker buffer += line 67*7594170eSAndroid Build Coastguard Worker if line.rstrip() == "}": 68*7594170eSAndroid Build Coastguard Worker assert in_module_def() 69*7594170eSAndroid Build Coastguard Worker # end of module definition 70*7594170eSAndroid Build Coastguard Worker yield module_type, buffer 71*7594170eSAndroid Build Coastguard Worker module_type = None 72*7594170eSAndroid Build Coastguard Worker buffer = "" 73*7594170eSAndroid Build Coastguard Worker 74*7594170eSAndroid Build Coastguard Worker 75*7594170eSAndroid Build Coastguard Workerdef type_in(*module_types: str) -> Filter: 76*7594170eSAndroid Build Coastguard Worker def f(t: ModuleType, _: ModuleName) -> bool: 77*7594170eSAndroid Build Coastguard Worker return t in module_types 78*7594170eSAndroid Build Coastguard Worker 79*7594170eSAndroid Build Coastguard Worker return f 80*7594170eSAndroid Build Coastguard Worker 81*7594170eSAndroid Build Coastguard Worker 82*7594170eSAndroid Build Coastguard Workerdef name_in(*module_names: str) -> Filter: 83*7594170eSAndroid Build Coastguard Worker def f(_: ModuleType, n: ModuleName) -> bool: 84*7594170eSAndroid Build Coastguard Worker return n in module_names 85*7594170eSAndroid Build Coastguard Worker 86*7594170eSAndroid Build Coastguard Worker return f 87*7594170eSAndroid Build Coastguard Worker 88*7594170eSAndroid Build Coastguard Worker 89*7594170eSAndroid Build Coastguard Workerdef _modify_genrule_template(module_name: ModuleName, module_def: str) -> Optional[str]: 90*7594170eSAndroid Build Coastguard Worker # assume `out` only occurs as top-level property of a module 91*7594170eSAndroid Build Coastguard Worker # assume `out` is always a singleton array 92*7594170eSAndroid Build Coastguard Worker p = re.compile(r'[\n\r]\s+out\s*:\s*\[[\n\r]*\s*"[^"]+(?=")', re.MULTILINE) 93*7594170eSAndroid Build Coastguard Worker g = p.search(module_def) 94*7594170eSAndroid Build Coastguard Worker if g is None: 95*7594170eSAndroid Build Coastguard Worker logging.debug('Could not find "out" for "%s"', module_name) 96*7594170eSAndroid Build Coastguard Worker return None 97*7594170eSAndroid Build Coastguard Worker index = g.end() 98*7594170eSAndroid Build Coastguard Worker return f"{module_def[: index]}-${{suffix}}{module_def[index:]}" 99*7594170eSAndroid Build Coastguard Worker 100*7594170eSAndroid Build Coastguard Worker 101*7594170eSAndroid Build Coastguard Workerdef _extract_templates_helper( 102*7594170eSAndroid Build Coastguard Worker src_lines: TextIO, f: Filter 103*7594170eSAndroid Build Coastguard Worker) -> dict[ModuleName, Template]: 104*7594170eSAndroid Build Coastguard Worker """ 105*7594170eSAndroid Build Coastguard Worker For `src_lines` from an Android.bp file, find modules that satisfy the 106*7594170eSAndroid Build Coastguard Worker Filter `f` and for each such mach return a "template" text that facilitates 107*7594170eSAndroid Build Coastguard Worker changing the module's name. 108*7594170eSAndroid Build Coastguard Worker """ 109*7594170eSAndroid Build Coastguard Worker # assume `name` only occurs as top-level property of a module 110*7594170eSAndroid Build Coastguard Worker name_pattern = re.compile(r'[\n\r]\s+name:\s*"(?P<name>[^"]+)(?=")', re.MULTILINE) 111*7594170eSAndroid Build Coastguard Worker result = dict[ModuleName, Template]() 112*7594170eSAndroid Build Coastguard Worker for module_type, module_def in module_defs(src_lines): 113*7594170eSAndroid Build Coastguard Worker m = name_pattern.search(module_def) 114*7594170eSAndroid Build Coastguard Worker if not m: 115*7594170eSAndroid Build Coastguard Worker continue 116*7594170eSAndroid Build Coastguard Worker module_name = ModuleName(m.group("name")) 117*7594170eSAndroid Build Coastguard Worker if module_name in result: 118*7594170eSAndroid Build Coastguard Worker logging.debug( 119*7594170eSAndroid Build Coastguard Worker f"{module_name} already exists thus " f"ignoring {module_type}" 120*7594170eSAndroid Build Coastguard Worker ) 121*7594170eSAndroid Build Coastguard Worker continue 122*7594170eSAndroid Build Coastguard Worker if not f(module_type, module_name): 123*7594170eSAndroid Build Coastguard Worker continue 124*7594170eSAndroid Build Coastguard Worker i = m.end() 125*7594170eSAndroid Build Coastguard Worker module_def = f"{module_def[: i]}-${{suffix}}{module_def[i:]}" 126*7594170eSAndroid Build Coastguard Worker if module_type == ModuleType("genrule"): 127*7594170eSAndroid Build Coastguard Worker module_def = _modify_genrule_template(module_name, module_def) 128*7594170eSAndroid Build Coastguard Worker if module_def is None: 129*7594170eSAndroid Build Coastguard Worker continue 130*7594170eSAndroid Build Coastguard Worker result[module_name] = Template(module_def) 131*7594170eSAndroid Build Coastguard Worker return result 132*7594170eSAndroid Build Coastguard Worker 133*7594170eSAndroid Build Coastguard Worker 134*7594170eSAndroid Build Coastguard Workerdef _extract_templates( 135*7594170eSAndroid Build Coastguard Worker bps: dict[Path, Filter] 136*7594170eSAndroid Build Coastguard Worker) -> dict[Path, dict[ModuleName, Template]]: 137*7594170eSAndroid Build Coastguard Worker """ 138*7594170eSAndroid Build Coastguard Worker If any key is a directory instead of an Android.bp file, expand it is as if it 139*7594170eSAndroid Build Coastguard Worker were the glob pattern $key/**/Android.bp, i.e. replace it with all Android.bp 140*7594170eSAndroid Build Coastguard Worker files under its tree. 141*7594170eSAndroid Build Coastguard Worker """ 142*7594170eSAndroid Build Coastguard Worker bp2templates = dict[Path, dict[ModuleName, Template]]() 143*7594170eSAndroid Build Coastguard Worker with open(src(_ALLOWLISTS), "rt") as af: 144*7594170eSAndroid Build Coastguard Worker go_allowlists = GoAllowlistManipulator(af.readlines()) 145*7594170eSAndroid Build Coastguard Worker alwaysconvert = go_allowlists.locate("Bp2buildModuleAlwaysConvertList") 146*7594170eSAndroid Build Coastguard Worker 147*7594170eSAndroid Build Coastguard Worker def maybe_register(bp: Path): 148*7594170eSAndroid Build Coastguard Worker with open(bp, "rt") as src_lines: 149*7594170eSAndroid Build Coastguard Worker templates = _extract_templates_helper(src_lines, fltr) 150*7594170eSAndroid Build Coastguard Worker if not go_allowlists.is_dir_allowed(bp.parent): 151*7594170eSAndroid Build Coastguard Worker templates = {n: v for n, v in templates.items() if n in alwaysconvert} 152*7594170eSAndroid Build Coastguard Worker if len(templates) == 0: 153*7594170eSAndroid Build Coastguard Worker logging.debug("No matches in %s", k) 154*7594170eSAndroid Build Coastguard Worker else: 155*7594170eSAndroid Build Coastguard Worker bp2templates[bp] = bp2templates.get(bp, {}) | templates 156*7594170eSAndroid Build Coastguard Worker 157*7594170eSAndroid Build Coastguard Worker for k, fltr in bps.items(): 158*7594170eSAndroid Build Coastguard Worker if k.name == "Android.bp": 159*7594170eSAndroid Build Coastguard Worker maybe_register(k) 160*7594170eSAndroid Build Coastguard Worker for root, _, _ in os.walk(k): 161*7594170eSAndroid Build Coastguard Worker if Path(root).is_relative_to(util.get_out_dir()): 162*7594170eSAndroid Build Coastguard Worker continue 163*7594170eSAndroid Build Coastguard Worker file = Path(root).joinpath("Android.bp") 164*7594170eSAndroid Build Coastguard Worker if file.exists(): 165*7594170eSAndroid Build Coastguard Worker maybe_register(file) 166*7594170eSAndroid Build Coastguard Worker 167*7594170eSAndroid Build Coastguard Worker return bp2templates 168*7594170eSAndroid Build Coastguard Worker 169*7594170eSAndroid Build Coastguard Worker 170*7594170eSAndroid Build Coastguard Worker@functools.cache 171*7594170eSAndroid Build Coastguard Workerdef _back_up_path() -> Path: 172*7594170eSAndroid Build Coastguard Worker # a directory to back up files that these CUJs change 173*7594170eSAndroid Build Coastguard Worker return util.get_out_dir().joinpath("clone-cuj-backup") 174*7594170eSAndroid Build Coastguard Worker 175*7594170eSAndroid Build Coastguard Worker 176*7594170eSAndroid Build Coastguard Workerdef _backup(bps: Iterable[Path]): 177*7594170eSAndroid Build Coastguard Worker # if first cuj_step then back up files to restore later 178*7594170eSAndroid Build Coastguard Worker if _back_up_path().exists(): 179*7594170eSAndroid Build Coastguard Worker raise RuntimeError( 180*7594170eSAndroid Build Coastguard Worker f"{_back_up_path()} already exists. " 181*7594170eSAndroid Build Coastguard Worker f"Did you kill a previous cuj run? " 182*7594170eSAndroid Build Coastguard Worker f"Delete {_back_up_path()} and revert changes to " 183*7594170eSAndroid Build Coastguard Worker f"allowlists.go and Android.bp files" 184*7594170eSAndroid Build Coastguard Worker ) 185*7594170eSAndroid Build Coastguard Worker for bp in bps: 186*7594170eSAndroid Build Coastguard Worker src_path = bp.relative_to(util.get_top_dir()) 187*7594170eSAndroid Build Coastguard Worker bak_file = _back_up_path().joinpath(src_path) 188*7594170eSAndroid Build Coastguard Worker os.makedirs(os.path.dirname(bak_file)) 189*7594170eSAndroid Build Coastguard Worker shutil.copy(bp, bak_file) 190*7594170eSAndroid Build Coastguard Worker src_allowlists = src(_ALLOWLISTS) 191*7594170eSAndroid Build Coastguard Worker bak_allowlists = _back_up_path().joinpath(_ALLOWLISTS) 192*7594170eSAndroid Build Coastguard Worker os.makedirs(os.path.dirname(bak_allowlists)) 193*7594170eSAndroid Build Coastguard Worker shutil.copy(src_allowlists, bak_allowlists) 194*7594170eSAndroid Build Coastguard Worker 195*7594170eSAndroid Build Coastguard Worker 196*7594170eSAndroid Build Coastguard Workerdef _restore(): 197*7594170eSAndroid Build Coastguard Worker src(_ALLOWLISTS).touch(exist_ok=True) 198*7594170eSAndroid Build Coastguard Worker for root, _, files in os.walk(_back_up_path()): 199*7594170eSAndroid Build Coastguard Worker for file in files: 200*7594170eSAndroid Build Coastguard Worker bak_file = Path(root).joinpath(file) 201*7594170eSAndroid Build Coastguard Worker bak_path = bak_file.relative_to(_back_up_path()) 202*7594170eSAndroid Build Coastguard Worker src_file = util.get_top_dir().joinpath(bak_path) 203*7594170eSAndroid Build Coastguard Worker shutil.copy(bak_file, src_file) 204*7594170eSAndroid Build Coastguard Worker # touch to update mtime; ctime is ignored by ninja 205*7594170eSAndroid Build Coastguard Worker src_file.touch(exist_ok=True) 206*7594170eSAndroid Build Coastguard Worker 207*7594170eSAndroid Build Coastguard Worker 208*7594170eSAndroid Build Coastguard Workerdef _bz_counterpart(bp: Path) -> Path: 209*7594170eSAndroid Build Coastguard Worker return ( 210*7594170eSAndroid Build Coastguard Worker util.get_out_dir() 211*7594170eSAndroid Build Coastguard Worker .joinpath("soong", "bp2build", bp.relative_to(util.get_top_dir())) 212*7594170eSAndroid Build Coastguard Worker .with_name("BUILD.bazel") 213*7594170eSAndroid Build Coastguard Worker ) 214*7594170eSAndroid Build Coastguard Worker 215*7594170eSAndroid Build Coastguard Worker 216*7594170eSAndroid Build Coastguard Workerdef _make_clones(bp2templates: dict[Path, dict[ModuleName, Template]], n: int): 217*7594170eSAndroid Build Coastguard Worker r = f"{str(uuid.uuid4()):.6}" # cache-busting 218*7594170eSAndroid Build Coastguard Worker source_count = 0 219*7594170eSAndroid Build Coastguard Worker output = ["\n"] 220*7594170eSAndroid Build Coastguard Worker 221*7594170eSAndroid Build Coastguard Worker with open(src(_ALLOWLISTS), "rt") as f: 222*7594170eSAndroid Build Coastguard Worker go_allowlists = GoAllowlistManipulator(f.readlines()) 223*7594170eSAndroid Build Coastguard Worker mixed_build_enabled_list = go_allowlists.locate("ProdMixedBuildsEnabledList") 224*7594170eSAndroid Build Coastguard Worker alwaysconvert = go_allowlists.locate("Bp2buildModuleAlwaysConvertList") 225*7594170eSAndroid Build Coastguard Worker 226*7594170eSAndroid Build Coastguard Worker def _allow(): 227*7594170eSAndroid Build Coastguard Worker if name not in mixed_build_enabled_list: 228*7594170eSAndroid Build Coastguard Worker mixed_build_enabled_list.prepend([name]) 229*7594170eSAndroid Build Coastguard Worker mixed_build_enabled_list.prepend(clones) 230*7594170eSAndroid Build Coastguard Worker 231*7594170eSAndroid Build Coastguard Worker if name in alwaysconvert: 232*7594170eSAndroid Build Coastguard Worker alwaysconvert.prepend(clones) 233*7594170eSAndroid Build Coastguard Worker 234*7594170eSAndroid Build Coastguard Worker for bp, n2t in bp2templates.items(): 235*7594170eSAndroid Build Coastguard Worker source_count += len(n2t) 236*7594170eSAndroid Build Coastguard Worker output.append( 237*7594170eSAndroid Build Coastguard Worker f"{n:5,}X{len(n2t):2,} modules = {n * len(n2t):+5,} " 238*7594170eSAndroid Build Coastguard Worker f"in {bp.relative_to(util.get_top_dir())}" 239*7594170eSAndroid Build Coastguard Worker ) 240*7594170eSAndroid Build Coastguard Worker with open(bp, "a") as f: 241*7594170eSAndroid Build Coastguard Worker for name, t in n2t.items(): 242*7594170eSAndroid Build Coastguard Worker clones = [] 243*7594170eSAndroid Build Coastguard Worker for n in range(1, n + 1): 244*7594170eSAndroid Build Coastguard Worker suffix = f"{r}-{n:05d}" 245*7594170eSAndroid Build Coastguard Worker f.write(t.substitute(suffix=suffix)) 246*7594170eSAndroid Build Coastguard Worker clones.append(ModuleName(f"{name}-{suffix}")) 247*7594170eSAndroid Build Coastguard Worker _allow() 248*7594170eSAndroid Build Coastguard Worker 249*7594170eSAndroid Build Coastguard Worker with open(src(_ALLOWLISTS), "wt") as f: 250*7594170eSAndroid Build Coastguard Worker f.writelines(go_allowlists.lines) 251*7594170eSAndroid Build Coastguard Worker 252*7594170eSAndroid Build Coastguard Worker logging.info( 253*7594170eSAndroid Build Coastguard Worker f"Cloned {n:,}X{source_count:,} modules = {n * source_count:+,} " 254*7594170eSAndroid Build Coastguard Worker f"in {len(bp2templates)} Android.bp files" 255*7594170eSAndroid Build Coastguard Worker ) 256*7594170eSAndroid Build Coastguard Worker logging.debug("\n".join(output)) 257*7594170eSAndroid Build Coastguard Worker 258*7594170eSAndroid Build Coastguard Worker 259*7594170eSAndroid Build Coastguard Workerdef _display_sizes(): 260*7594170eSAndroid Build Coastguard Worker file_count = 0 261*7594170eSAndroid Build Coastguard Worker orig_tot = 0 262*7594170eSAndroid Build Coastguard Worker curr_tot = 0 263*7594170eSAndroid Build Coastguard Worker output = ["\n"] 264*7594170eSAndroid Build Coastguard Worker for root, _, files in os.walk(_back_up_path()): 265*7594170eSAndroid Build Coastguard Worker file_count += len(files) 266*7594170eSAndroid Build Coastguard Worker for file in files: 267*7594170eSAndroid Build Coastguard Worker backup_file = Path(root).joinpath(file) 268*7594170eSAndroid Build Coastguard Worker common_path = backup_file.relative_to(_back_up_path()) 269*7594170eSAndroid Build Coastguard Worker source_file = util.get_top_dir().joinpath(common_path) 270*7594170eSAndroid Build Coastguard Worker curr_size = os.stat(source_file).st_size 271*7594170eSAndroid Build Coastguard Worker curr_tot += curr_size 272*7594170eSAndroid Build Coastguard Worker orig_size = os.stat(backup_file).st_size 273*7594170eSAndroid Build Coastguard Worker orig_tot += orig_size 274*7594170eSAndroid Build Coastguard Worker output.append( 275*7594170eSAndroid Build Coastguard Worker f"{orig_size:7,} {curr_size - orig_size :+5,} => {curr_size:9,} " 276*7594170eSAndroid Build Coastguard Worker f"bytes {source_file.relative_to(util.get_top_dir())}" 277*7594170eSAndroid Build Coastguard Worker ) 278*7594170eSAndroid Build Coastguard Worker if file == "Android.bp": 279*7594170eSAndroid Build Coastguard Worker bz = _bz_counterpart(source_file) 280*7594170eSAndroid Build Coastguard Worker output.append( 281*7594170eSAndroid Build Coastguard Worker f"{os.stat(bz).st_size:8,} bytes " 282*7594170eSAndroid Build Coastguard Worker f"$OUTDIR/{bz.relative_to(util.get_out_dir())}" 283*7594170eSAndroid Build Coastguard Worker ) 284*7594170eSAndroid Build Coastguard Worker logging.info( 285*7594170eSAndroid Build Coastguard Worker f"Affected {file_count} files {orig_tot:,} " 286*7594170eSAndroid Build Coastguard Worker f"{curr_tot - orig_tot:+,} => {curr_tot:,} bytes" 287*7594170eSAndroid Build Coastguard Worker ) 288*7594170eSAndroid Build Coastguard Worker logging.debug("\n".join(output)) 289*7594170eSAndroid Build Coastguard Worker 290*7594170eSAndroid Build Coastguard Worker 291*7594170eSAndroid Build Coastguard Workerdef _name_cuj(count: int, module_count: int, bp_count: int) -> str: 292*7594170eSAndroid Build Coastguard Worker match module_count: 293*7594170eSAndroid Build Coastguard Worker case 1: 294*7594170eSAndroid Build Coastguard Worker name = f"{count}" 295*7594170eSAndroid Build Coastguard Worker case _: 296*7594170eSAndroid Build Coastguard Worker name = f"{count}x{module_count}" 297*7594170eSAndroid Build Coastguard Worker if bp_count > 1: 298*7594170eSAndroid Build Coastguard Worker name = f"{name}({bp_count} files)" 299*7594170eSAndroid Build Coastguard Worker return name 300*7594170eSAndroid Build Coastguard Worker 301*7594170eSAndroid Build Coastguard Worker 302*7594170eSAndroid Build Coastguard Workerclass Clone(cuj.CujGroup): 303*7594170eSAndroid Build Coastguard Worker def __init__(self, group_name: str, bps: dict[Path, Filter]): 304*7594170eSAndroid Build Coastguard Worker super().__init__(group_name) 305*7594170eSAndroid Build Coastguard Worker self.bps = bps 306*7594170eSAndroid Build Coastguard Worker 307*7594170eSAndroid Build Coastguard Worker def get_steps(self) -> Iterable[cuj.CujStep]: 308*7594170eSAndroid Build Coastguard Worker bp2templates = _extract_templates(self.bps) 309*7594170eSAndroid Build Coastguard Worker bp_count = len(bp2templates) 310*7594170eSAndroid Build Coastguard Worker if bp_count == 0: 311*7594170eSAndroid Build Coastguard Worker raise RuntimeError(f"No eligible module to clone in {self.bps.keys()}") 312*7594170eSAndroid Build Coastguard Worker module_count = sum(len(templates) for templates in bp2templates.values()) 313*7594170eSAndroid Build Coastguard Worker 314*7594170eSAndroid Build Coastguard Worker if "CLONE" in os.environ: 315*7594170eSAndroid Build Coastguard Worker counts = [int(s) for s in os.environ["CLONE"].split(",")] 316*7594170eSAndroid Build Coastguard Worker else: 317*7594170eSAndroid Build Coastguard Worker counts = [1, 100, 200, 300, 400] 318*7594170eSAndroid Build Coastguard Worker logging.info( 319*7594170eSAndroid Build Coastguard Worker f'Will clone {",".join(str(i) for i in counts)} in cujs. ' 320*7594170eSAndroid Build Coastguard Worker f"You may specify alternative counts with CLONE env var, " 321*7594170eSAndroid Build Coastguard Worker f"e.g. CLONE = 1,10,100,1000" 322*7594170eSAndroid Build Coastguard Worker ) 323*7594170eSAndroid Build Coastguard Worker first_bp = next(iter(bp2templates.keys())) 324*7594170eSAndroid Build Coastguard Worker 325*7594170eSAndroid Build Coastguard Worker def modify_bp(): 326*7594170eSAndroid Build Coastguard Worker with open(first_bp, mode="a") as f: 327*7594170eSAndroid Build Coastguard Worker f.write(f"//post clone modification {uuid.uuid4()}\n") 328*7594170eSAndroid Build Coastguard Worker 329*7594170eSAndroid Build Coastguard Worker steps: list[cuj.CujStep] = [] 330*7594170eSAndroid Build Coastguard Worker for i, count in enumerate(counts): 331*7594170eSAndroid Build Coastguard Worker base_name = _name_cuj(count, module_count, bp_count) 332*7594170eSAndroid Build Coastguard Worker steps.append( 333*7594170eSAndroid Build Coastguard Worker cuj.CujStep( 334*7594170eSAndroid Build Coastguard Worker verb=base_name, 335*7594170eSAndroid Build Coastguard Worker apply_change=cuj.sequence( 336*7594170eSAndroid Build Coastguard Worker functools.partial(_backup, bp2templates.keys()) 337*7594170eSAndroid Build Coastguard Worker if i == 0 338*7594170eSAndroid Build Coastguard Worker else _restore, 339*7594170eSAndroid Build Coastguard Worker functools.partial(_make_clones, bp2templates, count), 340*7594170eSAndroid Build Coastguard Worker ), 341*7594170eSAndroid Build Coastguard Worker verify=_display_sizes, 342*7594170eSAndroid Build Coastguard Worker ) 343*7594170eSAndroid Build Coastguard Worker ) 344*7594170eSAndroid Build Coastguard Worker steps.append(cuj.CujStep(verb=f"bp aft {base_name}", apply_change=modify_bp)) 345*7594170eSAndroid Build Coastguard Worker if i == len(counts) - 1: 346*7594170eSAndroid Build Coastguard Worker steps.append( 347*7594170eSAndroid Build Coastguard Worker cuj.CujStep( 348*7594170eSAndroid Build Coastguard Worker verb="revert", 349*7594170eSAndroid Build Coastguard Worker apply_change=cuj.sequence( 350*7594170eSAndroid Build Coastguard Worker _restore, lambda: shutil.rmtree(_back_up_path()) 351*7594170eSAndroid Build Coastguard Worker ), 352*7594170eSAndroid Build Coastguard Worker verify=_display_sizes, 353*7594170eSAndroid Build Coastguard Worker ) 354*7594170eSAndroid Build Coastguard Worker ) 355*7594170eSAndroid Build Coastguard Worker return steps 356*7594170eSAndroid Build Coastguard Worker 357*7594170eSAndroid Build Coastguard Worker 358*7594170eSAndroid Build Coastguard Workerdef main(): 359*7594170eSAndroid Build Coastguard Worker """ 360*7594170eSAndroid Build Coastguard Worker provided only for manual run; 361*7594170eSAndroid Build Coastguard Worker use incremental_build.sh to invoke the cuj instead 362*7594170eSAndroid Build Coastguard Worker """ 363*7594170eSAndroid Build Coastguard Worker p = argparse.ArgumentParser() 364*7594170eSAndroid Build Coastguard Worker p.add_argument( 365*7594170eSAndroid Build Coastguard Worker "--module", 366*7594170eSAndroid Build Coastguard Worker "-m", 367*7594170eSAndroid Build Coastguard Worker default="adbd", 368*7594170eSAndroid Build Coastguard Worker help="name of the module to clone; default=%(default)s", 369*7594170eSAndroid Build Coastguard Worker ) 370*7594170eSAndroid Build Coastguard Worker p.add_argument( 371*7594170eSAndroid Build Coastguard Worker "--count", 372*7594170eSAndroid Build Coastguard Worker "-n", 373*7594170eSAndroid Build Coastguard Worker default=1, 374*7594170eSAndroid Build Coastguard Worker type=int, 375*7594170eSAndroid Build Coastguard Worker help="number of times to clone; default: %(default)s", 376*7594170eSAndroid Build Coastguard Worker ) 377*7594170eSAndroid Build Coastguard Worker adb_bp = util.get_top_dir().joinpath("packages/modules/adb/Android.bp") 378*7594170eSAndroid Build Coastguard Worker p.add_argument( 379*7594170eSAndroid Build Coastguard Worker "androidbp", 380*7594170eSAndroid Build Coastguard Worker nargs="?", 381*7594170eSAndroid Build Coastguard Worker default=adb_bp, 382*7594170eSAndroid Build Coastguard Worker type=Path, 383*7594170eSAndroid Build Coastguard Worker help="absolute path to Android.bp file; default=%(default)s", 384*7594170eSAndroid Build Coastguard Worker ) 385*7594170eSAndroid Build Coastguard Worker options = p.parse_args() 386*7594170eSAndroid Build Coastguard Worker _make_clones( 387*7594170eSAndroid Build Coastguard Worker _extract_templates({options.androidbp: name_in(options.module)}), options.count 388*7594170eSAndroid Build Coastguard Worker ) 389*7594170eSAndroid Build Coastguard Worker logging.warning("Changes made to your source tree; TIP: `repo status`") 390*7594170eSAndroid Build Coastguard Worker 391*7594170eSAndroid Build Coastguard Worker 392*7594170eSAndroid Build Coastguard Workerif __name__ == "__main__": 393*7594170eSAndroid Build Coastguard Worker logging.root.setLevel(logging.INFO) 394*7594170eSAndroid Build Coastguard Worker main() 395