1*333d2b36SAndroid Build Coastguard Worker#!/usr/bin/env -S python -u 2*333d2b36SAndroid Build Coastguard Worker# 3*333d2b36SAndroid Build Coastguard Worker# Copyright (C) 2022 The Android Open Source Project 4*333d2b36SAndroid Build Coastguard Worker# 5*333d2b36SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*333d2b36SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*333d2b36SAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*333d2b36SAndroid Build Coastguard Worker# 9*333d2b36SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*333d2b36SAndroid Build Coastguard Worker# 11*333d2b36SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*333d2b36SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*333d2b36SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*333d2b36SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*333d2b36SAndroid Build Coastguard Worker# limitations under the License. 16*333d2b36SAndroid Build Coastguard Worker"""Analyze bootclasspath_fragment usage.""" 17*333d2b36SAndroid Build Coastguard Workerimport argparse 18*333d2b36SAndroid Build Coastguard Workerimport dataclasses 19*333d2b36SAndroid Build Coastguard Workerimport enum 20*333d2b36SAndroid Build Coastguard Workerimport json 21*333d2b36SAndroid Build Coastguard Workerimport logging 22*333d2b36SAndroid Build Coastguard Workerimport os 23*333d2b36SAndroid Build Coastguard Workerimport re 24*333d2b36SAndroid Build Coastguard Workerimport shutil 25*333d2b36SAndroid Build Coastguard Workerimport subprocess 26*333d2b36SAndroid Build Coastguard Workerimport tempfile 27*333d2b36SAndroid Build Coastguard Workerimport textwrap 28*333d2b36SAndroid Build Coastguard Workerimport typing 29*333d2b36SAndroid Build Coastguard Workerfrom enum import Enum 30*333d2b36SAndroid Build Coastguard Worker 31*333d2b36SAndroid Build Coastguard Workerimport sys 32*333d2b36SAndroid Build Coastguard Worker 33*333d2b36SAndroid Build Coastguard Workerfrom signature_trie import signature_trie 34*333d2b36SAndroid Build Coastguard Worker 35*333d2b36SAndroid Build Coastguard Worker_STUB_FLAGS_FILE = "out/soong/hiddenapi/hiddenapi-stub-flags.txt" 36*333d2b36SAndroid Build Coastguard Worker 37*333d2b36SAndroid Build Coastguard Worker_FLAGS_FILE = "out/soong/hiddenapi/hiddenapi-flags.csv" 38*333d2b36SAndroid Build Coastguard Worker 39*333d2b36SAndroid Build Coastguard Worker_INCONSISTENT_FLAGS = "ERROR: Hidden API flags are inconsistent:" 40*333d2b36SAndroid Build Coastguard Worker 41*333d2b36SAndroid Build Coastguard Worker 42*333d2b36SAndroid Build Coastguard Workerclass BuildOperation: 43*333d2b36SAndroid Build Coastguard Worker 44*333d2b36SAndroid Build Coastguard Worker def __init__(self, popen): 45*333d2b36SAndroid Build Coastguard Worker self.popen = popen 46*333d2b36SAndroid Build Coastguard Worker self.returncode = None 47*333d2b36SAndroid Build Coastguard Worker 48*333d2b36SAndroid Build Coastguard Worker def lines(self): 49*333d2b36SAndroid Build Coastguard Worker """Return an iterator over the lines output by the build operation. 50*333d2b36SAndroid Build Coastguard Worker 51*333d2b36SAndroid Build Coastguard Worker The lines have had any trailing white space, including the newline 52*333d2b36SAndroid Build Coastguard Worker stripped. 53*333d2b36SAndroid Build Coastguard Worker """ 54*333d2b36SAndroid Build Coastguard Worker return newline_stripping_iter(self.popen.stdout.readline) 55*333d2b36SAndroid Build Coastguard Worker 56*333d2b36SAndroid Build Coastguard Worker def wait(self, *args, **kwargs): 57*333d2b36SAndroid Build Coastguard Worker self.popen.wait(*args, **kwargs) 58*333d2b36SAndroid Build Coastguard Worker self.returncode = self.popen.returncode 59*333d2b36SAndroid Build Coastguard Worker 60*333d2b36SAndroid Build Coastguard Worker 61*333d2b36SAndroid Build Coastguard Worker@dataclasses.dataclass() 62*333d2b36SAndroid Build Coastguard Workerclass FlagDiffs: 63*333d2b36SAndroid Build Coastguard Worker """Encapsulates differences in flags reported by the build""" 64*333d2b36SAndroid Build Coastguard Worker 65*333d2b36SAndroid Build Coastguard Worker # Map from member signature to the (module flags, monolithic flags) 66*333d2b36SAndroid Build Coastguard Worker diffs: typing.Dict[str, typing.Tuple[str, str]] 67*333d2b36SAndroid Build Coastguard Worker 68*333d2b36SAndroid Build Coastguard Worker 69*333d2b36SAndroid Build Coastguard Worker@dataclasses.dataclass() 70*333d2b36SAndroid Build Coastguard Workerclass ModuleInfo: 71*333d2b36SAndroid Build Coastguard Worker """Provides access to the generated module-info.json file. 72*333d2b36SAndroid Build Coastguard Worker 73*333d2b36SAndroid Build Coastguard Worker This is used to find the location of the file within which specific modules 74*333d2b36SAndroid Build Coastguard Worker are defined. 75*333d2b36SAndroid Build Coastguard Worker """ 76*333d2b36SAndroid Build Coastguard Worker 77*333d2b36SAndroid Build Coastguard Worker modules: typing.Dict[str, typing.Dict[str, typing.Any]] 78*333d2b36SAndroid Build Coastguard Worker 79*333d2b36SAndroid Build Coastguard Worker @staticmethod 80*333d2b36SAndroid Build Coastguard Worker def load(filename): 81*333d2b36SAndroid Build Coastguard Worker with open(filename, "r", encoding="utf8") as f: 82*333d2b36SAndroid Build Coastguard Worker j = json.load(f) 83*333d2b36SAndroid Build Coastguard Worker return ModuleInfo(j) 84*333d2b36SAndroid Build Coastguard Worker 85*333d2b36SAndroid Build Coastguard Worker def _module(self, module_name): 86*333d2b36SAndroid Build Coastguard Worker """Find module by name in module-info.json file""" 87*333d2b36SAndroid Build Coastguard Worker if module_name in self.modules: 88*333d2b36SAndroid Build Coastguard Worker return self.modules[module_name] 89*333d2b36SAndroid Build Coastguard Worker 90*333d2b36SAndroid Build Coastguard Worker raise Exception(f"Module {module_name} could not be found") 91*333d2b36SAndroid Build Coastguard Worker 92*333d2b36SAndroid Build Coastguard Worker def module_path(self, module_name): 93*333d2b36SAndroid Build Coastguard Worker module = self._module(module_name) 94*333d2b36SAndroid Build Coastguard Worker # The "path" is actually a list of paths, one for each class of module 95*333d2b36SAndroid Build Coastguard Worker # but as the modules are all created from bp files if a module does 96*333d2b36SAndroid Build Coastguard Worker # create multiple classes of make modules they should all have the same 97*333d2b36SAndroid Build Coastguard Worker # path. 98*333d2b36SAndroid Build Coastguard Worker paths = module["path"] 99*333d2b36SAndroid Build Coastguard Worker unique_paths = set(paths) 100*333d2b36SAndroid Build Coastguard Worker if len(unique_paths) != 1: 101*333d2b36SAndroid Build Coastguard Worker raise Exception(f"Expected module '{module_name}' to have a " 102*333d2b36SAndroid Build Coastguard Worker f"single unique path but found {unique_paths}") 103*333d2b36SAndroid Build Coastguard Worker return paths[0] 104*333d2b36SAndroid Build Coastguard Worker 105*333d2b36SAndroid Build Coastguard Worker 106*333d2b36SAndroid Build Coastguard Workerdef extract_indent(line): 107*333d2b36SAndroid Build Coastguard Worker return re.match(r"([ \t]*)", line).group(1) 108*333d2b36SAndroid Build Coastguard Worker 109*333d2b36SAndroid Build Coastguard Worker 110*333d2b36SAndroid Build Coastguard Worker_SPECIAL_PLACEHOLDER: str = "SPECIAL_PLACEHOLDER" 111*333d2b36SAndroid Build Coastguard Worker 112*333d2b36SAndroid Build Coastguard Worker 113*333d2b36SAndroid Build Coastguard Worker@dataclasses.dataclass 114*333d2b36SAndroid Build Coastguard Workerclass BpModifyRunner: 115*333d2b36SAndroid Build Coastguard Worker 116*333d2b36SAndroid Build Coastguard Worker bpmodify_path: str 117*333d2b36SAndroid Build Coastguard Worker 118*333d2b36SAndroid Build Coastguard Worker def add_values_to_property(self, property_name, values, module_name, 119*333d2b36SAndroid Build Coastguard Worker bp_file): 120*333d2b36SAndroid Build Coastguard Worker cmd = [ 121*333d2b36SAndroid Build Coastguard Worker self.bpmodify_path, "-a", values, "-property", property_name, "-m", 122*333d2b36SAndroid Build Coastguard Worker module_name, "-w", bp_file, bp_file 123*333d2b36SAndroid Build Coastguard Worker ] 124*333d2b36SAndroid Build Coastguard Worker 125*333d2b36SAndroid Build Coastguard Worker logging.debug(" ".join(cmd)) 126*333d2b36SAndroid Build Coastguard Worker subprocess.run( 127*333d2b36SAndroid Build Coastguard Worker cmd, 128*333d2b36SAndroid Build Coastguard Worker stderr=subprocess.STDOUT, 129*333d2b36SAndroid Build Coastguard Worker stdout=log_stream_for_subprocess(), 130*333d2b36SAndroid Build Coastguard Worker check=True) 131*333d2b36SAndroid Build Coastguard Worker 132*333d2b36SAndroid Build Coastguard Worker 133*333d2b36SAndroid Build Coastguard Worker@dataclasses.dataclass 134*333d2b36SAndroid Build Coastguard Workerclass FileChange: 135*333d2b36SAndroid Build Coastguard Worker path: str 136*333d2b36SAndroid Build Coastguard Worker 137*333d2b36SAndroid Build Coastguard Worker description: str 138*333d2b36SAndroid Build Coastguard Worker 139*333d2b36SAndroid Build Coastguard Worker def __lt__(self, other): 140*333d2b36SAndroid Build Coastguard Worker return self.path < other.path 141*333d2b36SAndroid Build Coastguard Worker 142*333d2b36SAndroid Build Coastguard Worker 143*333d2b36SAndroid Build Coastguard Workerclass PropertyChangeAction(Enum): 144*333d2b36SAndroid Build Coastguard Worker """Allowable actions that are supported by HiddenApiPropertyChange.""" 145*333d2b36SAndroid Build Coastguard Worker 146*333d2b36SAndroid Build Coastguard Worker # New values are appended to any existing values. 147*333d2b36SAndroid Build Coastguard Worker APPEND = 1 148*333d2b36SAndroid Build Coastguard Worker 149*333d2b36SAndroid Build Coastguard Worker # New values replace any existing values. 150*333d2b36SAndroid Build Coastguard Worker REPLACE = 2 151*333d2b36SAndroid Build Coastguard Worker 152*333d2b36SAndroid Build Coastguard Worker 153*333d2b36SAndroid Build Coastguard Worker@dataclasses.dataclass 154*333d2b36SAndroid Build Coastguard Workerclass HiddenApiPropertyChange: 155*333d2b36SAndroid Build Coastguard Worker 156*333d2b36SAndroid Build Coastguard Worker property_name: str 157*333d2b36SAndroid Build Coastguard Worker 158*333d2b36SAndroid Build Coastguard Worker values: typing.List[str] 159*333d2b36SAndroid Build Coastguard Worker 160*333d2b36SAndroid Build Coastguard Worker property_comment: str = "" 161*333d2b36SAndroid Build Coastguard Worker 162*333d2b36SAndroid Build Coastguard Worker # The action that indicates how this change is applied. 163*333d2b36SAndroid Build Coastguard Worker action: PropertyChangeAction = PropertyChangeAction.APPEND 164*333d2b36SAndroid Build Coastguard Worker 165*333d2b36SAndroid Build Coastguard Worker def snippet(self, indent): 166*333d2b36SAndroid Build Coastguard Worker snippet = "\n" 167*333d2b36SAndroid Build Coastguard Worker snippet += format_comment_as_text(self.property_comment, indent) 168*333d2b36SAndroid Build Coastguard Worker snippet += f"{indent}{self.property_name}: [" 169*333d2b36SAndroid Build Coastguard Worker if self.values: 170*333d2b36SAndroid Build Coastguard Worker snippet += "\n" 171*333d2b36SAndroid Build Coastguard Worker for value in self.values: 172*333d2b36SAndroid Build Coastguard Worker snippet += f'{indent} "{value}",\n' 173*333d2b36SAndroid Build Coastguard Worker snippet += f"{indent}" 174*333d2b36SAndroid Build Coastguard Worker snippet += "],\n" 175*333d2b36SAndroid Build Coastguard Worker return snippet 176*333d2b36SAndroid Build Coastguard Worker 177*333d2b36SAndroid Build Coastguard Worker def fix_bp_file(self, bcpf_bp_file, bcpf, bpmodify_runner: BpModifyRunner): 178*333d2b36SAndroid Build Coastguard Worker # Add an additional placeholder value to identify the modification that 179*333d2b36SAndroid Build Coastguard Worker # bpmodify makes. 180*333d2b36SAndroid Build Coastguard Worker bpmodify_values = [_SPECIAL_PLACEHOLDER] 181*333d2b36SAndroid Build Coastguard Worker 182*333d2b36SAndroid Build Coastguard Worker if self.action == PropertyChangeAction.APPEND: 183*333d2b36SAndroid Build Coastguard Worker # If adding the values to the existing values then pass the new 184*333d2b36SAndroid Build Coastguard Worker # values to bpmodify. 185*333d2b36SAndroid Build Coastguard Worker bpmodify_values.extend(self.values) 186*333d2b36SAndroid Build Coastguard Worker elif self.action == PropertyChangeAction.REPLACE: 187*333d2b36SAndroid Build Coastguard Worker # If replacing the existing values then it is not possible to use 188*333d2b36SAndroid Build Coastguard Worker # bpmodify for that directly. It could be used twice to remove the 189*333d2b36SAndroid Build Coastguard Worker # existing property and then add a new one but that does not remove 190*333d2b36SAndroid Build Coastguard Worker # any related comments and loses the position of the existing 191*333d2b36SAndroid Build Coastguard Worker # property as the new property is always added to the end of the 192*333d2b36SAndroid Build Coastguard Worker # containing block. 193*333d2b36SAndroid Build Coastguard Worker # 194*333d2b36SAndroid Build Coastguard Worker # So, instead of passing the new values to bpmodify this this just 195*333d2b36SAndroid Build Coastguard Worker # adds an extra placeholder to force bpmodify to format the list 196*333d2b36SAndroid Build Coastguard Worker # across multiple lines to ensure a consistent structure for the 197*333d2b36SAndroid Build Coastguard Worker # code that removes all the existing values and adds the new ones. 198*333d2b36SAndroid Build Coastguard Worker # 199*333d2b36SAndroid Build Coastguard Worker # This placeholder has to be different to the other placeholder as 200*333d2b36SAndroid Build Coastguard Worker # bpmodify dedups values. 201*333d2b36SAndroid Build Coastguard Worker bpmodify_values.append(_SPECIAL_PLACEHOLDER + "_REPLACE") 202*333d2b36SAndroid Build Coastguard Worker else: 203*333d2b36SAndroid Build Coastguard Worker raise ValueError(f"unknown action {self.action}") 204*333d2b36SAndroid Build Coastguard Worker 205*333d2b36SAndroid Build Coastguard Worker packages = ",".join(bpmodify_values) 206*333d2b36SAndroid Build Coastguard Worker bpmodify_runner.add_values_to_property( 207*333d2b36SAndroid Build Coastguard Worker f"hidden_api.{self.property_name}", packages, bcpf, bcpf_bp_file) 208*333d2b36SAndroid Build Coastguard Worker 209*333d2b36SAndroid Build Coastguard Worker with open(bcpf_bp_file, "r", encoding="utf8") as tio: 210*333d2b36SAndroid Build Coastguard Worker lines = tio.readlines() 211*333d2b36SAndroid Build Coastguard Worker lines = [line.rstrip("\n") for line in lines] 212*333d2b36SAndroid Build Coastguard Worker 213*333d2b36SAndroid Build Coastguard Worker if self.fixup_bpmodify_changes(bcpf_bp_file, lines): 214*333d2b36SAndroid Build Coastguard Worker with open(bcpf_bp_file, "w", encoding="utf8") as tio: 215*333d2b36SAndroid Build Coastguard Worker for line in lines: 216*333d2b36SAndroid Build Coastguard Worker print(line, file=tio) 217*333d2b36SAndroid Build Coastguard Worker 218*333d2b36SAndroid Build Coastguard Worker def fixup_bpmodify_changes(self, bcpf_bp_file, lines): 219*333d2b36SAndroid Build Coastguard Worker """Fixup the output of bpmodify. 220*333d2b36SAndroid Build Coastguard Worker 221*333d2b36SAndroid Build Coastguard Worker The bpmodify tool does not support all the capabilities that this needs 222*333d2b36SAndroid Build Coastguard Worker so it is used to do what it can, including marking the place in the 223*333d2b36SAndroid Build Coastguard Worker Android.bp file where it makes its changes and then this gets passed a 224*333d2b36SAndroid Build Coastguard Worker list of lines from that file which it then modifies to complete the 225*333d2b36SAndroid Build Coastguard Worker change. 226*333d2b36SAndroid Build Coastguard Worker 227*333d2b36SAndroid Build Coastguard Worker This analyzes the list of lines to find the indices of the significant 228*333d2b36SAndroid Build Coastguard Worker lines and then applies some changes. As those changes can insert and 229*333d2b36SAndroid Build Coastguard Worker delete lines (changing the indices of following lines) the changes are 230*333d2b36SAndroid Build Coastguard Worker generally done in reverse order starting from the end and working 231*333d2b36SAndroid Build Coastguard Worker towards the beginning. That ensures that the changes do not invalidate 232*333d2b36SAndroid Build Coastguard Worker the indices of following lines. 233*333d2b36SAndroid Build Coastguard Worker """ 234*333d2b36SAndroid Build Coastguard Worker 235*333d2b36SAndroid Build Coastguard Worker # Find the line containing the placeholder that has been inserted. 236*333d2b36SAndroid Build Coastguard Worker place_holder_index = -1 237*333d2b36SAndroid Build Coastguard Worker for i, line in enumerate(lines): 238*333d2b36SAndroid Build Coastguard Worker if _SPECIAL_PLACEHOLDER in line: 239*333d2b36SAndroid Build Coastguard Worker place_holder_index = i 240*333d2b36SAndroid Build Coastguard Worker break 241*333d2b36SAndroid Build Coastguard Worker if place_holder_index == -1: 242*333d2b36SAndroid Build Coastguard Worker logging.debug("Could not find %s in %s", _SPECIAL_PLACEHOLDER, 243*333d2b36SAndroid Build Coastguard Worker bcpf_bp_file) 244*333d2b36SAndroid Build Coastguard Worker return False 245*333d2b36SAndroid Build Coastguard Worker 246*333d2b36SAndroid Build Coastguard Worker # Remove the place holder. Do this before inserting the comment as that 247*333d2b36SAndroid Build Coastguard Worker # would change the location of the place holder in the list. 248*333d2b36SAndroid Build Coastguard Worker place_holder_line = lines[place_holder_index] 249*333d2b36SAndroid Build Coastguard Worker if place_holder_line.endswith("],"): 250*333d2b36SAndroid Build Coastguard Worker place_holder_line = place_holder_line.replace( 251*333d2b36SAndroid Build Coastguard Worker f'"{_SPECIAL_PLACEHOLDER}"', "") 252*333d2b36SAndroid Build Coastguard Worker lines[place_holder_index] = place_holder_line 253*333d2b36SAndroid Build Coastguard Worker else: 254*333d2b36SAndroid Build Coastguard Worker del lines[place_holder_index] 255*333d2b36SAndroid Build Coastguard Worker 256*333d2b36SAndroid Build Coastguard Worker # Scan forward to the end of the property block to remove a blank line 257*333d2b36SAndroid Build Coastguard Worker # that bpmodify inserts. 258*333d2b36SAndroid Build Coastguard Worker end_property_array_index = -1 259*333d2b36SAndroid Build Coastguard Worker for i in range(place_holder_index, len(lines)): 260*333d2b36SAndroid Build Coastguard Worker line = lines[i] 261*333d2b36SAndroid Build Coastguard Worker if line.endswith("],"): 262*333d2b36SAndroid Build Coastguard Worker end_property_array_index = i 263*333d2b36SAndroid Build Coastguard Worker break 264*333d2b36SAndroid Build Coastguard Worker if end_property_array_index == -1: 265*333d2b36SAndroid Build Coastguard Worker logging.debug("Could not find end of property array in %s", 266*333d2b36SAndroid Build Coastguard Worker bcpf_bp_file) 267*333d2b36SAndroid Build Coastguard Worker return False 268*333d2b36SAndroid Build Coastguard Worker 269*333d2b36SAndroid Build Coastguard Worker # If bdmodify inserted a blank line afterwards then remove it. 270*333d2b36SAndroid Build Coastguard Worker if (not lines[end_property_array_index + 1] and 271*333d2b36SAndroid Build Coastguard Worker lines[end_property_array_index + 2].endswith("},")): 272*333d2b36SAndroid Build Coastguard Worker del lines[end_property_array_index + 1] 273*333d2b36SAndroid Build Coastguard Worker 274*333d2b36SAndroid Build Coastguard Worker # Scan back to find the preceding property line. 275*333d2b36SAndroid Build Coastguard Worker property_line_index = -1 276*333d2b36SAndroid Build Coastguard Worker for i in range(place_holder_index, 0, -1): 277*333d2b36SAndroid Build Coastguard Worker line = lines[i] 278*333d2b36SAndroid Build Coastguard Worker if line.lstrip().startswith(f"{self.property_name}: ["): 279*333d2b36SAndroid Build Coastguard Worker property_line_index = i 280*333d2b36SAndroid Build Coastguard Worker break 281*333d2b36SAndroid Build Coastguard Worker if property_line_index == -1: 282*333d2b36SAndroid Build Coastguard Worker logging.debug("Could not find property line in %s", bcpf_bp_file) 283*333d2b36SAndroid Build Coastguard Worker return False 284*333d2b36SAndroid Build Coastguard Worker 285*333d2b36SAndroid Build Coastguard Worker # If this change is replacing the existing values then they need to be 286*333d2b36SAndroid Build Coastguard Worker # removed and replaced with the new values. That will change the lines 287*333d2b36SAndroid Build Coastguard Worker # after the property but it is necessary to do here as the following 288*333d2b36SAndroid Build Coastguard Worker # code operates on earlier lines. 289*333d2b36SAndroid Build Coastguard Worker if self.action == PropertyChangeAction.REPLACE: 290*333d2b36SAndroid Build Coastguard Worker # This removes the existing values and replaces them with the new 291*333d2b36SAndroid Build Coastguard Worker # values. 292*333d2b36SAndroid Build Coastguard Worker indent = extract_indent(lines[property_line_index + 1]) 293*333d2b36SAndroid Build Coastguard Worker insert = [f'{indent}"{x}",' for x in self.values] 294*333d2b36SAndroid Build Coastguard Worker lines[property_line_index + 1:end_property_array_index] = insert 295*333d2b36SAndroid Build Coastguard Worker if not self.values: 296*333d2b36SAndroid Build Coastguard Worker # If the property has no values then merge the ], onto the 297*333d2b36SAndroid Build Coastguard Worker # same line as the property name. 298*333d2b36SAndroid Build Coastguard Worker del lines[property_line_index + 1] 299*333d2b36SAndroid Build Coastguard Worker lines[property_line_index] = lines[property_line_index] + "]," 300*333d2b36SAndroid Build Coastguard Worker 301*333d2b36SAndroid Build Coastguard Worker # Only insert a comment if the property does not already have a comment. 302*333d2b36SAndroid Build Coastguard Worker line_preceding_property = lines[(property_line_index - 1)] 303*333d2b36SAndroid Build Coastguard Worker if (self.property_comment and 304*333d2b36SAndroid Build Coastguard Worker not re.match("([ \t]+)// ", line_preceding_property)): 305*333d2b36SAndroid Build Coastguard Worker # Extract the indent from the property line and use it to format the 306*333d2b36SAndroid Build Coastguard Worker # comment. 307*333d2b36SAndroid Build Coastguard Worker indent = extract_indent(lines[property_line_index]) 308*333d2b36SAndroid Build Coastguard Worker comment_lines = format_comment_as_lines(self.property_comment, 309*333d2b36SAndroid Build Coastguard Worker indent) 310*333d2b36SAndroid Build Coastguard Worker 311*333d2b36SAndroid Build Coastguard Worker # If the line before the comment is not blank then insert an extra 312*333d2b36SAndroid Build Coastguard Worker # blank line at the beginning of the comment. 313*333d2b36SAndroid Build Coastguard Worker if line_preceding_property: 314*333d2b36SAndroid Build Coastguard Worker comment_lines.insert(0, "") 315*333d2b36SAndroid Build Coastguard Worker 316*333d2b36SAndroid Build Coastguard Worker # Insert the comment before the property. 317*333d2b36SAndroid Build Coastguard Worker lines[property_line_index:property_line_index] = comment_lines 318*333d2b36SAndroid Build Coastguard Worker return True 319*333d2b36SAndroid Build Coastguard Worker 320*333d2b36SAndroid Build Coastguard Worker 321*333d2b36SAndroid Build Coastguard Worker@dataclasses.dataclass() 322*333d2b36SAndroid Build Coastguard Workerclass PackagePropertyReason: 323*333d2b36SAndroid Build Coastguard Worker """Provides the reasons why a package was added to a specific property. 324*333d2b36SAndroid Build Coastguard Worker 325*333d2b36SAndroid Build Coastguard Worker A split package is one that contains classes from the bootclasspath_fragment 326*333d2b36SAndroid Build Coastguard Worker and other bootclasspath modules. So, for a split package this contains the 327*333d2b36SAndroid Build Coastguard Worker corresponding lists of classes. 328*333d2b36SAndroid Build Coastguard Worker 329*333d2b36SAndroid Build Coastguard Worker A single package is one that contains classes sub-packages from the 330*333d2b36SAndroid Build Coastguard Worker For a split package this contains a list of classes in that package that are 331*333d2b36SAndroid Build Coastguard Worker provided by the bootclasspath_fragment and a list of classes 332*333d2b36SAndroid Build Coastguard Worker """ 333*333d2b36SAndroid Build Coastguard Worker 334*333d2b36SAndroid Build Coastguard Worker # The list of classes/sub-packages that is provided by the 335*333d2b36SAndroid Build Coastguard Worker # bootclasspath_fragment. 336*333d2b36SAndroid Build Coastguard Worker bcpf: typing.List[str] 337*333d2b36SAndroid Build Coastguard Worker 338*333d2b36SAndroid Build Coastguard Worker # The list of classes/sub-packages that is provided by other modules on the 339*333d2b36SAndroid Build Coastguard Worker # bootclasspath. 340*333d2b36SAndroid Build Coastguard Worker other: typing.List[str] 341*333d2b36SAndroid Build Coastguard Worker 342*333d2b36SAndroid Build Coastguard Worker 343*333d2b36SAndroid Build Coastguard Worker@dataclasses.dataclass() 344*333d2b36SAndroid Build Coastguard Workerclass Result: 345*333d2b36SAndroid Build Coastguard Worker """Encapsulates the result of the analysis.""" 346*333d2b36SAndroid Build Coastguard Worker 347*333d2b36SAndroid Build Coastguard Worker # The diffs in the flags. 348*333d2b36SAndroid Build Coastguard Worker diffs: typing.Optional[FlagDiffs] = None 349*333d2b36SAndroid Build Coastguard Worker 350*333d2b36SAndroid Build Coastguard Worker # A map from package name to the reason why it belongs in the 351*333d2b36SAndroid Build Coastguard Worker # split_packages property. 352*333d2b36SAndroid Build Coastguard Worker split_packages: typing.Dict[str, PackagePropertyReason] = dataclasses.field( 353*333d2b36SAndroid Build Coastguard Worker default_factory=dict) 354*333d2b36SAndroid Build Coastguard Worker 355*333d2b36SAndroid Build Coastguard Worker # A map from package name to the reason why it belongs in the 356*333d2b36SAndroid Build Coastguard Worker # single_packages property. 357*333d2b36SAndroid Build Coastguard Worker single_packages: typing.Dict[str, 358*333d2b36SAndroid Build Coastguard Worker PackagePropertyReason] = dataclasses.field( 359*333d2b36SAndroid Build Coastguard Worker default_factory=dict) 360*333d2b36SAndroid Build Coastguard Worker 361*333d2b36SAndroid Build Coastguard Worker # The list of packages to add to the package_prefixes property. 362*333d2b36SAndroid Build Coastguard Worker package_prefixes: typing.List[str] = dataclasses.field(default_factory=list) 363*333d2b36SAndroid Build Coastguard Worker 364*333d2b36SAndroid Build Coastguard Worker # The bootclasspath_fragment hidden API properties changes. 365*333d2b36SAndroid Build Coastguard Worker property_changes: typing.List[HiddenApiPropertyChange] = dataclasses.field( 366*333d2b36SAndroid Build Coastguard Worker default_factory=list) 367*333d2b36SAndroid Build Coastguard Worker 368*333d2b36SAndroid Build Coastguard Worker # The list of file changes. 369*333d2b36SAndroid Build Coastguard Worker file_changes: typing.List[FileChange] = dataclasses.field( 370*333d2b36SAndroid Build Coastguard Worker default_factory=list) 371*333d2b36SAndroid Build Coastguard Worker 372*333d2b36SAndroid Build Coastguard Worker 373*333d2b36SAndroid Build Coastguard Workerclass ClassProvider(enum.Enum): 374*333d2b36SAndroid Build Coastguard Worker """The source of a class found during the hidden API processing""" 375*333d2b36SAndroid Build Coastguard Worker BCPF = "bcpf" 376*333d2b36SAndroid Build Coastguard Worker OTHER = "other" 377*333d2b36SAndroid Build Coastguard Worker 378*333d2b36SAndroid Build Coastguard Worker 379*333d2b36SAndroid Build Coastguard Worker# A fake member to use when using the signature trie to compute the package 380*333d2b36SAndroid Build Coastguard Worker# properties from hidden API flags. This is needed because while that 381*333d2b36SAndroid Build Coastguard Worker# computation only cares about classes the trie expects a class to be an 382*333d2b36SAndroid Build Coastguard Worker# interior node but without a member it makes the class a leaf node. That causes 383*333d2b36SAndroid Build Coastguard Worker# problems when analyzing inner classes as the outer class is a leaf node for 384*333d2b36SAndroid Build Coastguard Worker# its own entry but is used as an interior node for inner classes. 385*333d2b36SAndroid Build Coastguard Worker_FAKE_MEMBER = ";->fake()V" 386*333d2b36SAndroid Build Coastguard Worker 387*333d2b36SAndroid Build Coastguard Worker 388*333d2b36SAndroid Build Coastguard Worker@dataclasses.dataclass() 389*333d2b36SAndroid Build Coastguard Workerclass BcpfAnalyzer: 390*333d2b36SAndroid Build Coastguard Worker # Path to this tool. 391*333d2b36SAndroid Build Coastguard Worker tool_path: str 392*333d2b36SAndroid Build Coastguard Worker 393*333d2b36SAndroid Build Coastguard Worker # Directory pointed to by ANDROID_BUILD_OUT 394*333d2b36SAndroid Build Coastguard Worker top_dir: str 395*333d2b36SAndroid Build Coastguard Worker 396*333d2b36SAndroid Build Coastguard Worker # Directory pointed to by OUT_DIR of {top_dir}/out if that is not set. 397*333d2b36SAndroid Build Coastguard Worker out_dir: str 398*333d2b36SAndroid Build Coastguard Worker 399*333d2b36SAndroid Build Coastguard Worker # Directory pointed to by ANDROID_PRODUCT_OUT. 400*333d2b36SAndroid Build Coastguard Worker product_out_dir: str 401*333d2b36SAndroid Build Coastguard Worker 402*333d2b36SAndroid Build Coastguard Worker # The name of the bootclasspath_fragment module. 403*333d2b36SAndroid Build Coastguard Worker bcpf: str 404*333d2b36SAndroid Build Coastguard Worker 405*333d2b36SAndroid Build Coastguard Worker # The name of the apex module containing {bcpf}, only used for 406*333d2b36SAndroid Build Coastguard Worker # informational purposes. 407*333d2b36SAndroid Build Coastguard Worker apex: str 408*333d2b36SAndroid Build Coastguard Worker 409*333d2b36SAndroid Build Coastguard Worker # The name of the sdk module containing {bcpf}, only used for 410*333d2b36SAndroid Build Coastguard Worker # informational purposes. 411*333d2b36SAndroid Build Coastguard Worker sdk: str 412*333d2b36SAndroid Build Coastguard Worker 413*333d2b36SAndroid Build Coastguard Worker # If true then this will attempt to automatically fix any issues that are 414*333d2b36SAndroid Build Coastguard Worker # found. 415*333d2b36SAndroid Build Coastguard Worker fix: bool = False 416*333d2b36SAndroid Build Coastguard Worker 417*333d2b36SAndroid Build Coastguard Worker # All the signatures, loaded from all-flags.csv, initialized by 418*333d2b36SAndroid Build Coastguard Worker # load_all_flags(). 419*333d2b36SAndroid Build Coastguard Worker _signatures: typing.Set[str] = dataclasses.field(default_factory=set) 420*333d2b36SAndroid Build Coastguard Worker 421*333d2b36SAndroid Build Coastguard Worker # All the classes, loaded from all-flags.csv, initialized by 422*333d2b36SAndroid Build Coastguard Worker # load_all_flags(). 423*333d2b36SAndroid Build Coastguard Worker _classes: typing.Set[str] = dataclasses.field(default_factory=set) 424*333d2b36SAndroid Build Coastguard Worker 425*333d2b36SAndroid Build Coastguard Worker # Information loaded from module-info.json, initialized by 426*333d2b36SAndroid Build Coastguard Worker # load_module_info(). 427*333d2b36SAndroid Build Coastguard Worker module_info: ModuleInfo = None 428*333d2b36SAndroid Build Coastguard Worker 429*333d2b36SAndroid Build Coastguard Worker @staticmethod 430*333d2b36SAndroid Build Coastguard Worker def reformat_report_test(text): 431*333d2b36SAndroid Build Coastguard Worker return re.sub(r"(.)\n([^\s])", r"\1 \2", text) 432*333d2b36SAndroid Build Coastguard Worker 433*333d2b36SAndroid Build Coastguard Worker def report(self, text="", **kwargs): 434*333d2b36SAndroid Build Coastguard Worker # Concatenate lines that are not separated by a blank line together to 435*333d2b36SAndroid Build Coastguard Worker # eliminate formatting applied to the supplied text to adhere to python 436*333d2b36SAndroid Build Coastguard Worker # line length limitations. 437*333d2b36SAndroid Build Coastguard Worker text = self.reformat_report_test(text) 438*333d2b36SAndroid Build Coastguard Worker logging.info("%s", text, **kwargs) 439*333d2b36SAndroid Build Coastguard Worker 440*333d2b36SAndroid Build Coastguard Worker def report_dedent(self, text, **kwargs): 441*333d2b36SAndroid Build Coastguard Worker text = textwrap.dedent(text) 442*333d2b36SAndroid Build Coastguard Worker self.report(text, **kwargs) 443*333d2b36SAndroid Build Coastguard Worker 444*333d2b36SAndroid Build Coastguard Worker def run_command(self, cmd, *args, **kwargs): 445*333d2b36SAndroid Build Coastguard Worker cmd_line = " ".join(cmd) 446*333d2b36SAndroid Build Coastguard Worker logging.debug("Running %s", cmd_line) 447*333d2b36SAndroid Build Coastguard Worker subprocess.run( 448*333d2b36SAndroid Build Coastguard Worker cmd, 449*333d2b36SAndroid Build Coastguard Worker *args, 450*333d2b36SAndroid Build Coastguard Worker check=True, 451*333d2b36SAndroid Build Coastguard Worker cwd=self.top_dir, 452*333d2b36SAndroid Build Coastguard Worker stderr=subprocess.STDOUT, 453*333d2b36SAndroid Build Coastguard Worker stdout=log_stream_for_subprocess(), 454*333d2b36SAndroid Build Coastguard Worker text=True, 455*333d2b36SAndroid Build Coastguard Worker **kwargs) 456*333d2b36SAndroid Build Coastguard Worker 457*333d2b36SAndroid Build Coastguard Worker @property 458*333d2b36SAndroid Build Coastguard Worker def signatures(self): 459*333d2b36SAndroid Build Coastguard Worker if not self._signatures: 460*333d2b36SAndroid Build Coastguard Worker raise Exception("signatures has not been initialized") 461*333d2b36SAndroid Build Coastguard Worker return self._signatures 462*333d2b36SAndroid Build Coastguard Worker 463*333d2b36SAndroid Build Coastguard Worker @property 464*333d2b36SAndroid Build Coastguard Worker def classes(self): 465*333d2b36SAndroid Build Coastguard Worker if not self._classes: 466*333d2b36SAndroid Build Coastguard Worker raise Exception("classes has not been initialized") 467*333d2b36SAndroid Build Coastguard Worker return self._classes 468*333d2b36SAndroid Build Coastguard Worker 469*333d2b36SAndroid Build Coastguard Worker def load_all_flags(self): 470*333d2b36SAndroid Build Coastguard Worker all_flags = self.find_bootclasspath_fragment_output_file( 471*333d2b36SAndroid Build Coastguard Worker "all-flags.csv") 472*333d2b36SAndroid Build Coastguard Worker 473*333d2b36SAndroid Build Coastguard Worker # Extract the set of signatures and a separate set of classes produced 474*333d2b36SAndroid Build Coastguard Worker # by the bootclasspath_fragment. 475*333d2b36SAndroid Build Coastguard Worker with open(all_flags, "r", encoding="utf8") as f: 476*333d2b36SAndroid Build Coastguard Worker for line in newline_stripping_iter(f.readline): 477*333d2b36SAndroid Build Coastguard Worker signature = self.line_to_signature(line) 478*333d2b36SAndroid Build Coastguard Worker self._signatures.add(signature) 479*333d2b36SAndroid Build Coastguard Worker class_name = self.signature_to_class(signature) 480*333d2b36SAndroid Build Coastguard Worker self._classes.add(class_name) 481*333d2b36SAndroid Build Coastguard Worker 482*333d2b36SAndroid Build Coastguard Worker def load_module_info(self): 483*333d2b36SAndroid Build Coastguard Worker module_info_file = os.path.join(self.product_out_dir, 484*333d2b36SAndroid Build Coastguard Worker "module-info.json") 485*333d2b36SAndroid Build Coastguard Worker self.report(f"\nMaking sure that {module_info_file} is up to date.\n") 486*333d2b36SAndroid Build Coastguard Worker output = self.build_file_read_output(module_info_file) 487*333d2b36SAndroid Build Coastguard Worker lines = output.lines() 488*333d2b36SAndroid Build Coastguard Worker for line in lines: 489*333d2b36SAndroid Build Coastguard Worker logging.debug("%s", line) 490*333d2b36SAndroid Build Coastguard Worker output.wait(timeout=10) 491*333d2b36SAndroid Build Coastguard Worker if output.returncode: 492*333d2b36SAndroid Build Coastguard Worker raise Exception(f"Error building {module_info_file}") 493*333d2b36SAndroid Build Coastguard Worker abs_module_info_file = os.path.join(self.top_dir, module_info_file) 494*333d2b36SAndroid Build Coastguard Worker self.module_info = ModuleInfo.load(abs_module_info_file) 495*333d2b36SAndroid Build Coastguard Worker 496*333d2b36SAndroid Build Coastguard Worker @staticmethod 497*333d2b36SAndroid Build Coastguard Worker def line_to_signature(line): 498*333d2b36SAndroid Build Coastguard Worker return line.split(",")[0] 499*333d2b36SAndroid Build Coastguard Worker 500*333d2b36SAndroid Build Coastguard Worker @staticmethod 501*333d2b36SAndroid Build Coastguard Worker def signature_to_class(signature): 502*333d2b36SAndroid Build Coastguard Worker return signature.split(";->")[0] 503*333d2b36SAndroid Build Coastguard Worker 504*333d2b36SAndroid Build Coastguard Worker @staticmethod 505*333d2b36SAndroid Build Coastguard Worker def to_parent_package(pkg_or_class): 506*333d2b36SAndroid Build Coastguard Worker return pkg_or_class.rsplit("/", 1)[0] 507*333d2b36SAndroid Build Coastguard Worker 508*333d2b36SAndroid Build Coastguard Worker def module_path(self, module_name): 509*333d2b36SAndroid Build Coastguard Worker return self.module_info.module_path(module_name) 510*333d2b36SAndroid Build Coastguard Worker 511*333d2b36SAndroid Build Coastguard Worker def module_out_dir(self, module_name): 512*333d2b36SAndroid Build Coastguard Worker module_path = self.module_path(module_name) 513*333d2b36SAndroid Build Coastguard Worker return os.path.join(self.out_dir, "soong/.intermediates", module_path, 514*333d2b36SAndroid Build Coastguard Worker module_name) 515*333d2b36SAndroid Build Coastguard Worker 516*333d2b36SAndroid Build Coastguard Worker def find_bootclasspath_fragment_output_file(self, basename, required=True): 517*333d2b36SAndroid Build Coastguard Worker # Find the output file of the bootclasspath_fragment with the specified 518*333d2b36SAndroid Build Coastguard Worker # base name. 519*333d2b36SAndroid Build Coastguard Worker found_file = "" 520*333d2b36SAndroid Build Coastguard Worker bcpf_out_dir = self.module_out_dir(self.bcpf) 521*333d2b36SAndroid Build Coastguard Worker for (dirpath, _, filenames) in os.walk(bcpf_out_dir): 522*333d2b36SAndroid Build Coastguard Worker for f in filenames: 523*333d2b36SAndroid Build Coastguard Worker if f == basename: 524*333d2b36SAndroid Build Coastguard Worker found_file = os.path.join(dirpath, f) 525*333d2b36SAndroid Build Coastguard Worker break 526*333d2b36SAndroid Build Coastguard Worker if not found_file and required: 527*333d2b36SAndroid Build Coastguard Worker raise Exception(f"Could not find {basename} in {bcpf_out_dir}") 528*333d2b36SAndroid Build Coastguard Worker return found_file 529*333d2b36SAndroid Build Coastguard Worker 530*333d2b36SAndroid Build Coastguard Worker def analyze(self): 531*333d2b36SAndroid Build Coastguard Worker """Analyze a bootclasspath_fragment module. 532*333d2b36SAndroid Build Coastguard Worker 533*333d2b36SAndroid Build Coastguard Worker Provides help in resolving any existing issues and provides 534*333d2b36SAndroid Build Coastguard Worker optimizations that can be applied. 535*333d2b36SAndroid Build Coastguard Worker """ 536*333d2b36SAndroid Build Coastguard Worker self.report(f"Analyzing bootclasspath_fragment module {self.bcpf}") 537*333d2b36SAndroid Build Coastguard Worker self.report_dedent(f""" 538*333d2b36SAndroid Build Coastguard Worker Run this tool to help initialize a bootclasspath_fragment module. 539*333d2b36SAndroid Build Coastguard Worker Before you start make sure that: 540*333d2b36SAndroid Build Coastguard Worker 541*333d2b36SAndroid Build Coastguard Worker 1. The current checkout is up to date. 542*333d2b36SAndroid Build Coastguard Worker 543*333d2b36SAndroid Build Coastguard Worker 2. The environment has been initialized using lunch, e.g. 544*333d2b36SAndroid Build Coastguard Worker lunch aosp_arm64-userdebug 545*333d2b36SAndroid Build Coastguard Worker 546*333d2b36SAndroid Build Coastguard Worker 3. You have added a bootclasspath_fragment module to the appropriate 547*333d2b36SAndroid Build Coastguard Worker Android.bp file. Something like this: 548*333d2b36SAndroid Build Coastguard Worker 549*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment {{ 550*333d2b36SAndroid Build Coastguard Worker name: "{self.bcpf}", 551*333d2b36SAndroid Build Coastguard Worker contents: [ 552*333d2b36SAndroid Build Coastguard Worker "...", 553*333d2b36SAndroid Build Coastguard Worker ], 554*333d2b36SAndroid Build Coastguard Worker 555*333d2b36SAndroid Build Coastguard Worker // The bootclasspath_fragments that provide APIs on which this 556*333d2b36SAndroid Build Coastguard Worker // depends. 557*333d2b36SAndroid Build Coastguard Worker fragments: [ 558*333d2b36SAndroid Build Coastguard Worker {{ 559*333d2b36SAndroid Build Coastguard Worker apex: "com.android.art", 560*333d2b36SAndroid Build Coastguard Worker module: "art-bootclasspath-fragment", 561*333d2b36SAndroid Build Coastguard Worker }}, 562*333d2b36SAndroid Build Coastguard Worker ], 563*333d2b36SAndroid Build Coastguard Worker }} 564*333d2b36SAndroid Build Coastguard Worker 565*333d2b36SAndroid Build Coastguard Worker 4. You have added it to the platform_bootclasspath module in 566*333d2b36SAndroid Build Coastguard Worker frameworks/base/boot/Android.bp. Something like this: 567*333d2b36SAndroid Build Coastguard Worker 568*333d2b36SAndroid Build Coastguard Worker platform_bootclasspath {{ 569*333d2b36SAndroid Build Coastguard Worker name: "platform-bootclasspath", 570*333d2b36SAndroid Build Coastguard Worker fragments: [ 571*333d2b36SAndroid Build Coastguard Worker ... 572*333d2b36SAndroid Build Coastguard Worker {{ 573*333d2b36SAndroid Build Coastguard Worker apex: "{self.apex}", 574*333d2b36SAndroid Build Coastguard Worker module: "{self.bcpf}", 575*333d2b36SAndroid Build Coastguard Worker }}, 576*333d2b36SAndroid Build Coastguard Worker ], 577*333d2b36SAndroid Build Coastguard Worker }} 578*333d2b36SAndroid Build Coastguard Worker 579*333d2b36SAndroid Build Coastguard Worker 5. You have added an sdk module. Something like this: 580*333d2b36SAndroid Build Coastguard Worker 581*333d2b36SAndroid Build Coastguard Worker sdk {{ 582*333d2b36SAndroid Build Coastguard Worker name: "{self.sdk}", 583*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragments: ["{self.bcpf}"], 584*333d2b36SAndroid Build Coastguard Worker }} 585*333d2b36SAndroid Build Coastguard Worker """) 586*333d2b36SAndroid Build Coastguard Worker 587*333d2b36SAndroid Build Coastguard Worker # Make sure that the module-info.json file is up to date. 588*333d2b36SAndroid Build Coastguard Worker self.load_module_info() 589*333d2b36SAndroid Build Coastguard Worker 590*333d2b36SAndroid Build Coastguard Worker self.report_dedent(""" 591*333d2b36SAndroid Build Coastguard Worker Cleaning potentially stale files. 592*333d2b36SAndroid Build Coastguard Worker """) 593*333d2b36SAndroid Build Coastguard Worker # Remove the out/soong/hiddenapi files. 594*333d2b36SAndroid Build Coastguard Worker shutil.rmtree(f"{self.out_dir}/soong/hiddenapi", ignore_errors=True) 595*333d2b36SAndroid Build Coastguard Worker 596*333d2b36SAndroid Build Coastguard Worker # Remove any bootclasspath_fragment output files. 597*333d2b36SAndroid Build Coastguard Worker shutil.rmtree(self.module_out_dir(self.bcpf), ignore_errors=True) 598*333d2b36SAndroid Build Coastguard Worker 599*333d2b36SAndroid Build Coastguard Worker self.build_monolithic_stubs_flags() 600*333d2b36SAndroid Build Coastguard Worker 601*333d2b36SAndroid Build Coastguard Worker result = Result() 602*333d2b36SAndroid Build Coastguard Worker 603*333d2b36SAndroid Build Coastguard Worker self.build_monolithic_flags(result) 604*333d2b36SAndroid Build Coastguard Worker self.analyze_hiddenapi_package_properties(result) 605*333d2b36SAndroid Build Coastguard Worker self.explain_how_to_check_signature_patterns() 606*333d2b36SAndroid Build Coastguard Worker 607*333d2b36SAndroid Build Coastguard Worker # If there were any changes that need to be made to the Android.bp 608*333d2b36SAndroid Build Coastguard Worker # file then either apply or report them. 609*333d2b36SAndroid Build Coastguard Worker if result.property_changes: 610*333d2b36SAndroid Build Coastguard Worker bcpf_dir = self.module_info.module_path(self.bcpf) 611*333d2b36SAndroid Build Coastguard Worker bcpf_bp_file = os.path.join(self.top_dir, bcpf_dir, "Android.bp") 612*333d2b36SAndroid Build Coastguard Worker if self.fix: 613*333d2b36SAndroid Build Coastguard Worker tool_dir = os.path.dirname(self.tool_path) 614*333d2b36SAndroid Build Coastguard Worker bpmodify_path = os.path.join(tool_dir, "bpmodify") 615*333d2b36SAndroid Build Coastguard Worker bpmodify_runner = BpModifyRunner(bpmodify_path) 616*333d2b36SAndroid Build Coastguard Worker for property_change in result.property_changes: 617*333d2b36SAndroid Build Coastguard Worker property_change.fix_bp_file(bcpf_bp_file, self.bcpf, 618*333d2b36SAndroid Build Coastguard Worker bpmodify_runner) 619*333d2b36SAndroid Build Coastguard Worker 620*333d2b36SAndroid Build Coastguard Worker result.file_changes.append( 621*333d2b36SAndroid Build Coastguard Worker self.new_file_change( 622*333d2b36SAndroid Build Coastguard Worker bcpf_bp_file, 623*333d2b36SAndroid Build Coastguard Worker f"Updated hidden_api properties of '{self.bcpf}'")) 624*333d2b36SAndroid Build Coastguard Worker 625*333d2b36SAndroid Build Coastguard Worker else: 626*333d2b36SAndroid Build Coastguard Worker hiddenapi_snippet = "" 627*333d2b36SAndroid Build Coastguard Worker for property_change in result.property_changes: 628*333d2b36SAndroid Build Coastguard Worker hiddenapi_snippet += property_change.snippet(" ") 629*333d2b36SAndroid Build Coastguard Worker 630*333d2b36SAndroid Build Coastguard Worker # Remove leading and trailing blank lines. 631*333d2b36SAndroid Build Coastguard Worker hiddenapi_snippet = hiddenapi_snippet.strip("\n") 632*333d2b36SAndroid Build Coastguard Worker 633*333d2b36SAndroid Build Coastguard Worker result.file_changes.append( 634*333d2b36SAndroid Build Coastguard Worker self.new_file_change( 635*333d2b36SAndroid Build Coastguard Worker bcpf_bp_file, f""" 636*333d2b36SAndroid Build Coastguard WorkerAdd the following snippet into the {self.bcpf} bootclasspath_fragment module 637*333d2b36SAndroid Build Coastguard Workerin the {bcpf_dir}/Android.bp file. If the hidden_api block already exists then 638*333d2b36SAndroid Build Coastguard Workermerge these properties into it. 639*333d2b36SAndroid Build Coastguard Worker 640*333d2b36SAndroid Build Coastguard Worker hidden_api: {{ 641*333d2b36SAndroid Build Coastguard Worker{hiddenapi_snippet} 642*333d2b36SAndroid Build Coastguard Worker }}, 643*333d2b36SAndroid Build Coastguard Worker""")) 644*333d2b36SAndroid Build Coastguard Worker 645*333d2b36SAndroid Build Coastguard Worker if result.file_changes: 646*333d2b36SAndroid Build Coastguard Worker if self.fix: 647*333d2b36SAndroid Build Coastguard Worker file_change_message = textwrap.dedent(""" 648*333d2b36SAndroid Build Coastguard Worker The following files were modified by this script: 649*333d2b36SAndroid Build Coastguard Worker """) 650*333d2b36SAndroid Build Coastguard Worker else: 651*333d2b36SAndroid Build Coastguard Worker file_change_message = textwrap.dedent(""" 652*333d2b36SAndroid Build Coastguard Worker The following modifications need to be made: 653*333d2b36SAndroid Build Coastguard Worker """) 654*333d2b36SAndroid Build Coastguard Worker 655*333d2b36SAndroid Build Coastguard Worker self.report(file_change_message) 656*333d2b36SAndroid Build Coastguard Worker result.file_changes.sort() 657*333d2b36SAndroid Build Coastguard Worker for file_change in result.file_changes: 658*333d2b36SAndroid Build Coastguard Worker self.report(f" {file_change.path}") 659*333d2b36SAndroid Build Coastguard Worker self.report(f" {file_change.description}") 660*333d2b36SAndroid Build Coastguard Worker self.report() 661*333d2b36SAndroid Build Coastguard Worker 662*333d2b36SAndroid Build Coastguard Worker if not self.fix: 663*333d2b36SAndroid Build Coastguard Worker self.report_dedent(""" 664*333d2b36SAndroid Build Coastguard Worker Run the command again with the --fix option to automatically 665*333d2b36SAndroid Build Coastguard Worker make the above changes. 666*333d2b36SAndroid Build Coastguard Worker """.lstrip("\n")) 667*333d2b36SAndroid Build Coastguard Worker 668*333d2b36SAndroid Build Coastguard Worker def new_file_change(self, file, description): 669*333d2b36SAndroid Build Coastguard Worker return FileChange( 670*333d2b36SAndroid Build Coastguard Worker path=os.path.relpath(file, self.top_dir), description=description) 671*333d2b36SAndroid Build Coastguard Worker 672*333d2b36SAndroid Build Coastguard Worker def check_inconsistent_flag_lines(self, significant, module_line, 673*333d2b36SAndroid Build Coastguard Worker monolithic_line, separator_line): 674*333d2b36SAndroid Build Coastguard Worker if not (module_line.startswith("< ") and 675*333d2b36SAndroid Build Coastguard Worker monolithic_line.startswith("> ") and not separator_line): 676*333d2b36SAndroid Build Coastguard Worker # Something went wrong. 677*333d2b36SAndroid Build Coastguard Worker self.report("Invalid build output detected:") 678*333d2b36SAndroid Build Coastguard Worker self.report(f" module_line: '{module_line}'") 679*333d2b36SAndroid Build Coastguard Worker self.report(f" monolithic_line: '{monolithic_line}'") 680*333d2b36SAndroid Build Coastguard Worker self.report(f" separator_line: '{separator_line}'") 681*333d2b36SAndroid Build Coastguard Worker sys.exit(1) 682*333d2b36SAndroid Build Coastguard Worker 683*333d2b36SAndroid Build Coastguard Worker if significant: 684*333d2b36SAndroid Build Coastguard Worker logging.debug("%s", module_line) 685*333d2b36SAndroid Build Coastguard Worker logging.debug("%s", monolithic_line) 686*333d2b36SAndroid Build Coastguard Worker logging.debug("%s", separator_line) 687*333d2b36SAndroid Build Coastguard Worker 688*333d2b36SAndroid Build Coastguard Worker def scan_inconsistent_flags_report(self, lines): 689*333d2b36SAndroid Build Coastguard Worker """Scans a hidden API flags report 690*333d2b36SAndroid Build Coastguard Worker 691*333d2b36SAndroid Build Coastguard Worker The hidden API inconsistent flags report which looks something like 692*333d2b36SAndroid Build Coastguard Worker this. 693*333d2b36SAndroid Build Coastguard Worker 694*333d2b36SAndroid Build Coastguard Worker < out/soong/.intermediates/.../filtered-stub-flags.csv 695*333d2b36SAndroid Build Coastguard Worker > out/soong/hiddenapi/hiddenapi-stub-flags.txt 696*333d2b36SAndroid Build Coastguard Worker 697*333d2b36SAndroid Build Coastguard Worker < Landroid/compat/Compatibility;->clearOverrides()V 698*333d2b36SAndroid Build Coastguard Worker > Landroid/compat/Compatibility;->clearOverrides()V,core-platform-api 699*333d2b36SAndroid Build Coastguard Worker 700*333d2b36SAndroid Build Coastguard Worker """ 701*333d2b36SAndroid Build Coastguard Worker 702*333d2b36SAndroid Build Coastguard Worker # The basic format of an entry in the inconsistent flags report is: 703*333d2b36SAndroid Build Coastguard Worker # <module specific flag> 704*333d2b36SAndroid Build Coastguard Worker # <monolithic flag> 705*333d2b36SAndroid Build Coastguard Worker # <separator> 706*333d2b36SAndroid Build Coastguard Worker # 707*333d2b36SAndroid Build Coastguard Worker # Wrap the lines iterator in an iterator which returns a tuple 708*333d2b36SAndroid Build Coastguard Worker # consisting of the three separate lines. 709*333d2b36SAndroid Build Coastguard Worker triples = zip(lines, lines, lines) 710*333d2b36SAndroid Build Coastguard Worker 711*333d2b36SAndroid Build Coastguard Worker module_line, monolithic_line, separator_line = next(triples) 712*333d2b36SAndroid Build Coastguard Worker significant = False 713*333d2b36SAndroid Build Coastguard Worker bcpf_dir = self.module_info.module_path(self.bcpf) 714*333d2b36SAndroid Build Coastguard Worker if os.path.join(bcpf_dir, self.bcpf) in module_line: 715*333d2b36SAndroid Build Coastguard Worker # These errors are related to the bcpf being analyzed so 716*333d2b36SAndroid Build Coastguard Worker # keep them. 717*333d2b36SAndroid Build Coastguard Worker significant = True 718*333d2b36SAndroid Build Coastguard Worker else: 719*333d2b36SAndroid Build Coastguard Worker self.report(f"Filtering out errors related to {module_line}") 720*333d2b36SAndroid Build Coastguard Worker 721*333d2b36SAndroid Build Coastguard Worker self.check_inconsistent_flag_lines(significant, module_line, 722*333d2b36SAndroid Build Coastguard Worker monolithic_line, separator_line) 723*333d2b36SAndroid Build Coastguard Worker 724*333d2b36SAndroid Build Coastguard Worker diffs = {} 725*333d2b36SAndroid Build Coastguard Worker for module_line, monolithic_line, separator_line in triples: 726*333d2b36SAndroid Build Coastguard Worker self.check_inconsistent_flag_lines(significant, module_line, 727*333d2b36SAndroid Build Coastguard Worker monolithic_line, "") 728*333d2b36SAndroid Build Coastguard Worker 729*333d2b36SAndroid Build Coastguard Worker module_parts = module_line.removeprefix("< ").split(",") 730*333d2b36SAndroid Build Coastguard Worker module_signature = module_parts[0] 731*333d2b36SAndroid Build Coastguard Worker module_flags = module_parts[1:] 732*333d2b36SAndroid Build Coastguard Worker 733*333d2b36SAndroid Build Coastguard Worker monolithic_parts = monolithic_line.removeprefix("> ").split(",") 734*333d2b36SAndroid Build Coastguard Worker monolithic_signature = monolithic_parts[0] 735*333d2b36SAndroid Build Coastguard Worker monolithic_flags = monolithic_parts[1:] 736*333d2b36SAndroid Build Coastguard Worker 737*333d2b36SAndroid Build Coastguard Worker if module_signature != monolithic_signature: 738*333d2b36SAndroid Build Coastguard Worker # Something went wrong. 739*333d2b36SAndroid Build Coastguard Worker self.report("Inconsistent signatures detected:") 740*333d2b36SAndroid Build Coastguard Worker self.report(f" module_signature: '{module_signature}'") 741*333d2b36SAndroid Build Coastguard Worker self.report(f" monolithic_signature: '{monolithic_signature}'") 742*333d2b36SAndroid Build Coastguard Worker sys.exit(1) 743*333d2b36SAndroid Build Coastguard Worker 744*333d2b36SAndroid Build Coastguard Worker diffs[module_signature] = (module_flags, monolithic_flags) 745*333d2b36SAndroid Build Coastguard Worker 746*333d2b36SAndroid Build Coastguard Worker if separator_line: 747*333d2b36SAndroid Build Coastguard Worker # If the separator line is not blank then it is the end of the 748*333d2b36SAndroid Build Coastguard Worker # current report, and possibly the start of another. 749*333d2b36SAndroid Build Coastguard Worker return separator_line, diffs 750*333d2b36SAndroid Build Coastguard Worker 751*333d2b36SAndroid Build Coastguard Worker return "", diffs 752*333d2b36SAndroid Build Coastguard Worker 753*333d2b36SAndroid Build Coastguard Worker def build_file_read_output(self, filename): 754*333d2b36SAndroid Build Coastguard Worker # Make sure the filename is relative to top if possible as the build 755*333d2b36SAndroid Build Coastguard Worker # may be using relative paths as the target. 756*333d2b36SAndroid Build Coastguard Worker rel_filename = filename.removeprefix(self.top_dir) 757*333d2b36SAndroid Build Coastguard Worker cmd = ["build/soong/soong_ui.bash", "--make-mode", rel_filename] 758*333d2b36SAndroid Build Coastguard Worker cmd_line = " ".join(cmd) 759*333d2b36SAndroid Build Coastguard Worker logging.debug("%s", cmd_line) 760*333d2b36SAndroid Build Coastguard Worker # pylint: disable=consider-using-with 761*333d2b36SAndroid Build Coastguard Worker output = subprocess.Popen( 762*333d2b36SAndroid Build Coastguard Worker cmd, 763*333d2b36SAndroid Build Coastguard Worker cwd=self.top_dir, 764*333d2b36SAndroid Build Coastguard Worker stderr=subprocess.STDOUT, 765*333d2b36SAndroid Build Coastguard Worker stdout=subprocess.PIPE, 766*333d2b36SAndroid Build Coastguard Worker text=True, 767*333d2b36SAndroid Build Coastguard Worker ) 768*333d2b36SAndroid Build Coastguard Worker return BuildOperation(popen=output) 769*333d2b36SAndroid Build Coastguard Worker 770*333d2b36SAndroid Build Coastguard Worker def build_hiddenapi_flags(self, filename): 771*333d2b36SAndroid Build Coastguard Worker output = self.build_file_read_output(filename) 772*333d2b36SAndroid Build Coastguard Worker 773*333d2b36SAndroid Build Coastguard Worker lines = output.lines() 774*333d2b36SAndroid Build Coastguard Worker diffs = None 775*333d2b36SAndroid Build Coastguard Worker for line in lines: 776*333d2b36SAndroid Build Coastguard Worker logging.debug("%s", line) 777*333d2b36SAndroid Build Coastguard Worker while line == _INCONSISTENT_FLAGS: 778*333d2b36SAndroid Build Coastguard Worker line, diffs = self.scan_inconsistent_flags_report(lines) 779*333d2b36SAndroid Build Coastguard Worker 780*333d2b36SAndroid Build Coastguard Worker output.wait(timeout=10) 781*333d2b36SAndroid Build Coastguard Worker if output.returncode != 0: 782*333d2b36SAndroid Build Coastguard Worker logging.debug("Command failed with %s", output.returncode) 783*333d2b36SAndroid Build Coastguard Worker else: 784*333d2b36SAndroid Build Coastguard Worker logging.debug("Command succeeded") 785*333d2b36SAndroid Build Coastguard Worker 786*333d2b36SAndroid Build Coastguard Worker return diffs 787*333d2b36SAndroid Build Coastguard Worker 788*333d2b36SAndroid Build Coastguard Worker def build_monolithic_stubs_flags(self): 789*333d2b36SAndroid Build Coastguard Worker self.report_dedent(f""" 790*333d2b36SAndroid Build Coastguard Worker Attempting to build {_STUB_FLAGS_FILE} to verify that the 791*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment has the correct API stubs available... 792*333d2b36SAndroid Build Coastguard Worker """) 793*333d2b36SAndroid Build Coastguard Worker 794*333d2b36SAndroid Build Coastguard Worker # Build the hiddenapi-stubs-flags.txt file. 795*333d2b36SAndroid Build Coastguard Worker diffs = self.build_hiddenapi_flags(_STUB_FLAGS_FILE) 796*333d2b36SAndroid Build Coastguard Worker if diffs: 797*333d2b36SAndroid Build Coastguard Worker self.report_dedent(f""" 798*333d2b36SAndroid Build Coastguard Worker There is a discrepancy between the stub API derived flags 799*333d2b36SAndroid Build Coastguard Worker created by the bootclasspath_fragment and the 800*333d2b36SAndroid Build Coastguard Worker platform_bootclasspath. See preceding error messages to see 801*333d2b36SAndroid Build Coastguard Worker which flags are inconsistent. The inconsistencies can occur for 802*333d2b36SAndroid Build Coastguard Worker a couple of reasons: 803*333d2b36SAndroid Build Coastguard Worker 804*333d2b36SAndroid Build Coastguard Worker If you are building against prebuilts of the Android SDK, e.g. 805*333d2b36SAndroid Build Coastguard Worker by using TARGET_BUILD_APPS then the prebuilt versions of the 806*333d2b36SAndroid Build Coastguard Worker APIs this bootclasspath_fragment depends upon are out of date 807*333d2b36SAndroid Build Coastguard Worker and need updating. See go/update-prebuilts for help. 808*333d2b36SAndroid Build Coastguard Worker 809*333d2b36SAndroid Build Coastguard Worker Otherwise, this is happening because there are some stub APIs 810*333d2b36SAndroid Build Coastguard Worker that are either provided by or used by the contents of the 811*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment but which are not available to it. There 812*333d2b36SAndroid Build Coastguard Worker are 4 ways to handle this: 813*333d2b36SAndroid Build Coastguard Worker 814*333d2b36SAndroid Build Coastguard Worker 1. A java_sdk_library in the contents property will 815*333d2b36SAndroid Build Coastguard Worker automatically make its stub APIs available to the 816*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment so nothing needs to be done. 817*333d2b36SAndroid Build Coastguard Worker 818*333d2b36SAndroid Build Coastguard Worker 2. If the API provided by the bootclasspath_fragment is created 819*333d2b36SAndroid Build Coastguard Worker by an api_only java_sdk_library (or a java_library that compiles 820*333d2b36SAndroid Build Coastguard Worker files generated by a separate droidstubs module then it cannot 821*333d2b36SAndroid Build Coastguard Worker be added to the contents and instead must be added to the 822*333d2b36SAndroid Build Coastguard Worker api.stubs property, e.g. 823*333d2b36SAndroid Build Coastguard Worker 824*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment {{ 825*333d2b36SAndroid Build Coastguard Worker name: "{self.bcpf}", 826*333d2b36SAndroid Build Coastguard Worker ... 827*333d2b36SAndroid Build Coastguard Worker api: {{ 828*333d2b36SAndroid Build Coastguard Worker stubs: ["$MODULE-api-only"]," 829*333d2b36SAndroid Build Coastguard Worker }}, 830*333d2b36SAndroid Build Coastguard Worker }} 831*333d2b36SAndroid Build Coastguard Worker 832*333d2b36SAndroid Build Coastguard Worker 3. If the contents use APIs provided by another 833*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment then it needs to be added to the 834*333d2b36SAndroid Build Coastguard Worker fragments property, e.g. 835*333d2b36SAndroid Build Coastguard Worker 836*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment {{ 837*333d2b36SAndroid Build Coastguard Worker name: "{self.bcpf}", 838*333d2b36SAndroid Build Coastguard Worker ... 839*333d2b36SAndroid Build Coastguard Worker // The bootclasspath_fragments that provide APIs on which this depends. 840*333d2b36SAndroid Build Coastguard Worker fragments: [ 841*333d2b36SAndroid Build Coastguard Worker ... 842*333d2b36SAndroid Build Coastguard Worker {{ 843*333d2b36SAndroid Build Coastguard Worker apex: "com.android.other", 844*333d2b36SAndroid Build Coastguard Worker module: "com.android.other-bootclasspath-fragment", 845*333d2b36SAndroid Build Coastguard Worker }}, 846*333d2b36SAndroid Build Coastguard Worker ], 847*333d2b36SAndroid Build Coastguard Worker }} 848*333d2b36SAndroid Build Coastguard Worker 849*333d2b36SAndroid Build Coastguard Worker 4. If the contents use APIs from a module that is not part of 850*333d2b36SAndroid Build Coastguard Worker another bootclasspath_fragment then it must be added to the 851*333d2b36SAndroid Build Coastguard Worker additional_stubs property, e.g. 852*333d2b36SAndroid Build Coastguard Worker 853*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment {{ 854*333d2b36SAndroid Build Coastguard Worker name: "{self.bcpf}", 855*333d2b36SAndroid Build Coastguard Worker ... 856*333d2b36SAndroid Build Coastguard Worker additional_stubs: ["android-non-updatable"], 857*333d2b36SAndroid Build Coastguard Worker }} 858*333d2b36SAndroid Build Coastguard Worker 859*333d2b36SAndroid Build Coastguard Worker Like the api.stubs property these are typically 860*333d2b36SAndroid Build Coastguard Worker java_sdk_library modules but can be java_library too. 861*333d2b36SAndroid Build Coastguard Worker 862*333d2b36SAndroid Build Coastguard Worker Note: The "android-non-updatable" is treated as if it was a 863*333d2b36SAndroid Build Coastguard Worker java_sdk_library which it is not at the moment but will be in 864*333d2b36SAndroid Build Coastguard Worker future. 865*333d2b36SAndroid Build Coastguard Worker """) 866*333d2b36SAndroid Build Coastguard Worker 867*333d2b36SAndroid Build Coastguard Worker return diffs 868*333d2b36SAndroid Build Coastguard Worker 869*333d2b36SAndroid Build Coastguard Worker def build_monolithic_flags(self, result): 870*333d2b36SAndroid Build Coastguard Worker self.report_dedent(f""" 871*333d2b36SAndroid Build Coastguard Worker Attempting to build {_FLAGS_FILE} to verify that the 872*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment has the correct hidden API flags... 873*333d2b36SAndroid Build Coastguard Worker """) 874*333d2b36SAndroid Build Coastguard Worker 875*333d2b36SAndroid Build Coastguard Worker # Build the hiddenapi-flags.csv file and extract any differences in 876*333d2b36SAndroid Build Coastguard Worker # the flags between this bootclasspath_fragment and the monolithic 877*333d2b36SAndroid Build Coastguard Worker # files. 878*333d2b36SAndroid Build Coastguard Worker result.diffs = self.build_hiddenapi_flags(_FLAGS_FILE) 879*333d2b36SAndroid Build Coastguard Worker 880*333d2b36SAndroid Build Coastguard Worker # Load information from the bootclasspath_fragment's all-flags.csv file. 881*333d2b36SAndroid Build Coastguard Worker self.load_all_flags() 882*333d2b36SAndroid Build Coastguard Worker 883*333d2b36SAndroid Build Coastguard Worker if result.diffs: 884*333d2b36SAndroid Build Coastguard Worker self.report_dedent(f""" 885*333d2b36SAndroid Build Coastguard Worker There is a discrepancy between the hidden API flags created by 886*333d2b36SAndroid Build Coastguard Worker the bootclasspath_fragment and the platform_bootclasspath. See 887*333d2b36SAndroid Build Coastguard Worker preceding error messages to see which flags are inconsistent. 888*333d2b36SAndroid Build Coastguard Worker The inconsistencies can occur for a couple of reasons: 889*333d2b36SAndroid Build Coastguard Worker 890*333d2b36SAndroid Build Coastguard Worker If you are building against prebuilts of this 891*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment then the prebuilt version of the sdk 892*333d2b36SAndroid Build Coastguard Worker snapshot (specifically the hidden API flag files) are 893*333d2b36SAndroid Build Coastguard Worker inconsistent with the prebuilt version of the apex {self.apex}. 894*333d2b36SAndroid Build Coastguard Worker Please ensure that they are both updated from the same build. 895*333d2b36SAndroid Build Coastguard Worker 896*333d2b36SAndroid Build Coastguard Worker 1. There are custom hidden API flags specified in the one of the 897*333d2b36SAndroid Build Coastguard Worker files in frameworks/base/boot/hiddenapi which apply to the 898*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment but which are not supplied to the 899*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment module. 900*333d2b36SAndroid Build Coastguard Worker 901*333d2b36SAndroid Build Coastguard Worker 2. The bootclasspath_fragment specifies invalid 902*333d2b36SAndroid Build Coastguard Worker "split_packages", "single_packages" and/of "package_prefixes" 903*333d2b36SAndroid Build Coastguard Worker properties that match packages and classes that it does not 904*333d2b36SAndroid Build Coastguard Worker provide. 905*333d2b36SAndroid Build Coastguard Worker """) 906*333d2b36SAndroid Build Coastguard Worker 907*333d2b36SAndroid Build Coastguard Worker # Check to see if there are any hiddenapi related properties that 908*333d2b36SAndroid Build Coastguard Worker # need to be added to the 909*333d2b36SAndroid Build Coastguard Worker self.report_dedent(""" 910*333d2b36SAndroid Build Coastguard Worker Checking custom hidden API flags.... 911*333d2b36SAndroid Build Coastguard Worker """) 912*333d2b36SAndroid Build Coastguard Worker self.check_frameworks_base_boot_hidden_api_files(result) 913*333d2b36SAndroid Build Coastguard Worker 914*333d2b36SAndroid Build Coastguard Worker def report_hidden_api_flag_file_changes(self, result, property_name, 915*333d2b36SAndroid Build Coastguard Worker flags_file, rel_bcpf_flags_file, 916*333d2b36SAndroid Build Coastguard Worker bcpf_flags_file): 917*333d2b36SAndroid Build Coastguard Worker matched_signatures = set() 918*333d2b36SAndroid Build Coastguard Worker # Open the flags file to read the flags from. 919*333d2b36SAndroid Build Coastguard Worker with open(flags_file, "r", encoding="utf8") as f: 920*333d2b36SAndroid Build Coastguard Worker for signature in newline_stripping_iter(f.readline): 921*333d2b36SAndroid Build Coastguard Worker if signature in self.signatures: 922*333d2b36SAndroid Build Coastguard Worker # The signature is provided by the bootclasspath_fragment so 923*333d2b36SAndroid Build Coastguard Worker # it will need to be moved to the bootclasspath_fragment 924*333d2b36SAndroid Build Coastguard Worker # specific file. 925*333d2b36SAndroid Build Coastguard Worker matched_signatures.add(signature) 926*333d2b36SAndroid Build Coastguard Worker 927*333d2b36SAndroid Build Coastguard Worker # If the bootclasspath_fragment specific flags file is not empty 928*333d2b36SAndroid Build Coastguard Worker # then it contains flags. That could either be new flags just moved 929*333d2b36SAndroid Build Coastguard Worker # from frameworks/base or previous contents of the file. In either 930*333d2b36SAndroid Build Coastguard Worker # case the file must not be removed. 931*333d2b36SAndroid Build Coastguard Worker if matched_signatures: 932*333d2b36SAndroid Build Coastguard Worker insert = textwrap.indent("\n".join(matched_signatures), 933*333d2b36SAndroid Build Coastguard Worker " ") 934*333d2b36SAndroid Build Coastguard Worker result.file_changes.append( 935*333d2b36SAndroid Build Coastguard Worker self.new_file_change( 936*333d2b36SAndroid Build Coastguard Worker flags_file, f"""Remove the following entries: 937*333d2b36SAndroid Build Coastguard Worker{insert} 938*333d2b36SAndroid Build Coastguard Worker""")) 939*333d2b36SAndroid Build Coastguard Worker 940*333d2b36SAndroid Build Coastguard Worker result.file_changes.append( 941*333d2b36SAndroid Build Coastguard Worker self.new_file_change( 942*333d2b36SAndroid Build Coastguard Worker bcpf_flags_file, f"""Add the following entries: 943*333d2b36SAndroid Build Coastguard Worker{insert} 944*333d2b36SAndroid Build Coastguard Worker""")) 945*333d2b36SAndroid Build Coastguard Worker 946*333d2b36SAndroid Build Coastguard Worker result.property_changes.append( 947*333d2b36SAndroid Build Coastguard Worker HiddenApiPropertyChange( 948*333d2b36SAndroid Build Coastguard Worker property_name=property_name, 949*333d2b36SAndroid Build Coastguard Worker values=[rel_bcpf_flags_file], 950*333d2b36SAndroid Build Coastguard Worker )) 951*333d2b36SAndroid Build Coastguard Worker 952*333d2b36SAndroid Build Coastguard Worker def fix_hidden_api_flag_files(self, result, property_name, flags_file, 953*333d2b36SAndroid Build Coastguard Worker rel_bcpf_flags_file, bcpf_flags_file): 954*333d2b36SAndroid Build Coastguard Worker # Read the file in frameworks/base/boot/hiddenapi/<file> copy any 955*333d2b36SAndroid Build Coastguard Worker # flags that relate to the bootclasspath_fragment into a local 956*333d2b36SAndroid Build Coastguard Worker # file in the hiddenapi subdirectory. 957*333d2b36SAndroid Build Coastguard Worker tmp_flags_file = flags_file + ".tmp" 958*333d2b36SAndroid Build Coastguard Worker 959*333d2b36SAndroid Build Coastguard Worker # Make sure the directory containing the bootclasspath_fragment specific 960*333d2b36SAndroid Build Coastguard Worker # hidden api flags exists. 961*333d2b36SAndroid Build Coastguard Worker os.makedirs(os.path.dirname(bcpf_flags_file), exist_ok=True) 962*333d2b36SAndroid Build Coastguard Worker 963*333d2b36SAndroid Build Coastguard Worker bcpf_flags_file_exists = os.path.exists(bcpf_flags_file) 964*333d2b36SAndroid Build Coastguard Worker 965*333d2b36SAndroid Build Coastguard Worker matched_signatures = set() 966*333d2b36SAndroid Build Coastguard Worker # Open the flags file to read the flags from. 967*333d2b36SAndroid Build Coastguard Worker with open(flags_file, "r", encoding="utf8") as f: 968*333d2b36SAndroid Build Coastguard Worker # Open a temporary file to write the flags (minus any removed 969*333d2b36SAndroid Build Coastguard Worker # flags). 970*333d2b36SAndroid Build Coastguard Worker with open(tmp_flags_file, "w", encoding="utf8") as t: 971*333d2b36SAndroid Build Coastguard Worker # Open the bootclasspath_fragment file for append just in 972*333d2b36SAndroid Build Coastguard Worker # case it already exists. 973*333d2b36SAndroid Build Coastguard Worker with open(bcpf_flags_file, "a", encoding="utf8") as b: 974*333d2b36SAndroid Build Coastguard Worker for line in iter(f.readline, ""): 975*333d2b36SAndroid Build Coastguard Worker signature = line.rstrip() 976*333d2b36SAndroid Build Coastguard Worker if signature in self.signatures: 977*333d2b36SAndroid Build Coastguard Worker # The signature is provided by the 978*333d2b36SAndroid Build Coastguard Worker # bootclasspath_fragment so write it to the new 979*333d2b36SAndroid Build Coastguard Worker # bootclasspath_fragment specific file. 980*333d2b36SAndroid Build Coastguard Worker print(line, file=b, end="") 981*333d2b36SAndroid Build Coastguard Worker matched_signatures.add(signature) 982*333d2b36SAndroid Build Coastguard Worker else: 983*333d2b36SAndroid Build Coastguard Worker # The signature is NOT provided by the 984*333d2b36SAndroid Build Coastguard Worker # bootclasspath_fragment. Copy it to the new 985*333d2b36SAndroid Build Coastguard Worker # monolithic file. 986*333d2b36SAndroid Build Coastguard Worker print(line, file=t, end="") 987*333d2b36SAndroid Build Coastguard Worker 988*333d2b36SAndroid Build Coastguard Worker # If the bootclasspath_fragment specific flags file is not empty 989*333d2b36SAndroid Build Coastguard Worker # then it contains flags. That could either be new flags just moved 990*333d2b36SAndroid Build Coastguard Worker # from frameworks/base or previous contents of the file. In either 991*333d2b36SAndroid Build Coastguard Worker # case the file must not be removed. 992*333d2b36SAndroid Build Coastguard Worker if matched_signatures: 993*333d2b36SAndroid Build Coastguard Worker # There are custom flags related to the bootclasspath_fragment 994*333d2b36SAndroid Build Coastguard Worker # so replace the frameworks/base/boot/hiddenapi file with the 995*333d2b36SAndroid Build Coastguard Worker # file that does not contain those flags. 996*333d2b36SAndroid Build Coastguard Worker shutil.move(tmp_flags_file, flags_file) 997*333d2b36SAndroid Build Coastguard Worker 998*333d2b36SAndroid Build Coastguard Worker result.file_changes.append( 999*333d2b36SAndroid Build Coastguard Worker self.new_file_change(flags_file, 1000*333d2b36SAndroid Build Coastguard Worker f"Removed '{self.bcpf}' specific entries")) 1001*333d2b36SAndroid Build Coastguard Worker 1002*333d2b36SAndroid Build Coastguard Worker result.property_changes.append( 1003*333d2b36SAndroid Build Coastguard Worker HiddenApiPropertyChange( 1004*333d2b36SAndroid Build Coastguard Worker property_name=property_name, 1005*333d2b36SAndroid Build Coastguard Worker values=[rel_bcpf_flags_file], 1006*333d2b36SAndroid Build Coastguard Worker )) 1007*333d2b36SAndroid Build Coastguard Worker 1008*333d2b36SAndroid Build Coastguard Worker # Make sure that the files are sorted. 1009*333d2b36SAndroid Build Coastguard Worker self.run_command([ 1010*333d2b36SAndroid Build Coastguard Worker "tools/platform-compat/hiddenapi/sort_api.sh", 1011*333d2b36SAndroid Build Coastguard Worker bcpf_flags_file, 1012*333d2b36SAndroid Build Coastguard Worker ]) 1013*333d2b36SAndroid Build Coastguard Worker 1014*333d2b36SAndroid Build Coastguard Worker if bcpf_flags_file_exists: 1015*333d2b36SAndroid Build Coastguard Worker desc = f"Added '{self.bcpf}' specific entries" 1016*333d2b36SAndroid Build Coastguard Worker else: 1017*333d2b36SAndroid Build Coastguard Worker desc = f"Created with '{self.bcpf}' specific entries" 1018*333d2b36SAndroid Build Coastguard Worker result.file_changes.append( 1019*333d2b36SAndroid Build Coastguard Worker self.new_file_change(bcpf_flags_file, desc)) 1020*333d2b36SAndroid Build Coastguard Worker else: 1021*333d2b36SAndroid Build Coastguard Worker # There are no custom flags related to the 1022*333d2b36SAndroid Build Coastguard Worker # bootclasspath_fragment so clean up the working files. 1023*333d2b36SAndroid Build Coastguard Worker os.remove(tmp_flags_file) 1024*333d2b36SAndroid Build Coastguard Worker if not bcpf_flags_file_exists: 1025*333d2b36SAndroid Build Coastguard Worker os.remove(bcpf_flags_file) 1026*333d2b36SAndroid Build Coastguard Worker 1027*333d2b36SAndroid Build Coastguard Worker def check_frameworks_base_boot_hidden_api_files(self, result): 1028*333d2b36SAndroid Build Coastguard Worker hiddenapi_dir = os.path.join(self.top_dir, 1029*333d2b36SAndroid Build Coastguard Worker "frameworks/base/boot/hiddenapi") 1030*333d2b36SAndroid Build Coastguard Worker for basename in sorted(os.listdir(hiddenapi_dir)): 1031*333d2b36SAndroid Build Coastguard Worker if not (basename.startswith("hiddenapi-") and 1032*333d2b36SAndroid Build Coastguard Worker basename.endswith(".txt")): 1033*333d2b36SAndroid Build Coastguard Worker continue 1034*333d2b36SAndroid Build Coastguard Worker 1035*333d2b36SAndroid Build Coastguard Worker flags_file = os.path.join(hiddenapi_dir, basename) 1036*333d2b36SAndroid Build Coastguard Worker 1037*333d2b36SAndroid Build Coastguard Worker logging.debug("Checking %s for flags related to %s", flags_file, 1038*333d2b36SAndroid Build Coastguard Worker self.bcpf) 1039*333d2b36SAndroid Build Coastguard Worker 1040*333d2b36SAndroid Build Coastguard Worker # Map the file name in frameworks/base/boot/hiddenapi into a 1041*333d2b36SAndroid Build Coastguard Worker # slightly more meaningful name for use by the 1042*333d2b36SAndroid Build Coastguard Worker # bootclasspath_fragment. 1043*333d2b36SAndroid Build Coastguard Worker if basename == "hiddenapi-max-target-o.txt": 1044*333d2b36SAndroid Build Coastguard Worker basename = "hiddenapi-max-target-o-low-priority.txt" 1045*333d2b36SAndroid Build Coastguard Worker elif basename == "hiddenapi-max-target-r-loprio.txt": 1046*333d2b36SAndroid Build Coastguard Worker basename = "hiddenapi-max-target-r-low-priority.txt" 1047*333d2b36SAndroid Build Coastguard Worker 1048*333d2b36SAndroid Build Coastguard Worker property_name = basename.removeprefix("hiddenapi-") 1049*333d2b36SAndroid Build Coastguard Worker property_name = property_name.removesuffix(".txt") 1050*333d2b36SAndroid Build Coastguard Worker property_name = property_name.replace("-", "_") 1051*333d2b36SAndroid Build Coastguard Worker 1052*333d2b36SAndroid Build Coastguard Worker rel_bcpf_flags_file = f"hiddenapi/{basename}" 1053*333d2b36SAndroid Build Coastguard Worker bcpf_dir = self.module_info.module_path(self.bcpf) 1054*333d2b36SAndroid Build Coastguard Worker bcpf_flags_file = os.path.join(self.top_dir, bcpf_dir, 1055*333d2b36SAndroid Build Coastguard Worker rel_bcpf_flags_file) 1056*333d2b36SAndroid Build Coastguard Worker 1057*333d2b36SAndroid Build Coastguard Worker if self.fix: 1058*333d2b36SAndroid Build Coastguard Worker self.fix_hidden_api_flag_files(result, property_name, 1059*333d2b36SAndroid Build Coastguard Worker flags_file, rel_bcpf_flags_file, 1060*333d2b36SAndroid Build Coastguard Worker bcpf_flags_file) 1061*333d2b36SAndroid Build Coastguard Worker else: 1062*333d2b36SAndroid Build Coastguard Worker self.report_hidden_api_flag_file_changes( 1063*333d2b36SAndroid Build Coastguard Worker result, property_name, flags_file, rel_bcpf_flags_file, 1064*333d2b36SAndroid Build Coastguard Worker bcpf_flags_file) 1065*333d2b36SAndroid Build Coastguard Worker 1066*333d2b36SAndroid Build Coastguard Worker @staticmethod 1067*333d2b36SAndroid Build Coastguard Worker def split_package_comment(split_packages): 1068*333d2b36SAndroid Build Coastguard Worker if split_packages: 1069*333d2b36SAndroid Build Coastguard Worker return textwrap.dedent(""" 1070*333d2b36SAndroid Build Coastguard Worker The following packages contain classes from other modules on the 1071*333d2b36SAndroid Build Coastguard Worker bootclasspath. That means that the hidden API flags for this 1072*333d2b36SAndroid Build Coastguard Worker module has to explicitly list every single class this module 1073*333d2b36SAndroid Build Coastguard Worker provides in that package to differentiate them from the classes 1074*333d2b36SAndroid Build Coastguard Worker provided by other modules. That can include private classes that 1075*333d2b36SAndroid Build Coastguard Worker are not part of the API. 1076*333d2b36SAndroid Build Coastguard Worker """).strip("\n") 1077*333d2b36SAndroid Build Coastguard Worker 1078*333d2b36SAndroid Build Coastguard Worker return "This module does not contain any split packages." 1079*333d2b36SAndroid Build Coastguard Worker 1080*333d2b36SAndroid Build Coastguard Worker @staticmethod 1081*333d2b36SAndroid Build Coastguard Worker def package_prefixes_comment(): 1082*333d2b36SAndroid Build Coastguard Worker return textwrap.dedent(""" 1083*333d2b36SAndroid Build Coastguard Worker The following packages and all their subpackages currently only 1084*333d2b36SAndroid Build Coastguard Worker contain classes from this bootclasspath_fragment. Listing a package 1085*333d2b36SAndroid Build Coastguard Worker here won't prevent other bootclasspath modules from adding classes 1086*333d2b36SAndroid Build Coastguard Worker in any of those packages but it will prevent them from adding those 1087*333d2b36SAndroid Build Coastguard Worker classes into an API surface, e.g. public, system, etc.. Doing so 1088*333d2b36SAndroid Build Coastguard Worker will result in a build failure due to inconsistent flags. 1089*333d2b36SAndroid Build Coastguard Worker """).strip("\n") 1090*333d2b36SAndroid Build Coastguard Worker 1091*333d2b36SAndroid Build Coastguard Worker def analyze_hiddenapi_package_properties(self, result): 1092*333d2b36SAndroid Build Coastguard Worker self.compute_hiddenapi_package_properties(result) 1093*333d2b36SAndroid Build Coastguard Worker 1094*333d2b36SAndroid Build Coastguard Worker def indent_lines(lines): 1095*333d2b36SAndroid Build Coastguard Worker return "\n".join([f" {cls}" for cls in lines]) 1096*333d2b36SAndroid Build Coastguard Worker 1097*333d2b36SAndroid Build Coastguard Worker # TODO(b/202154151): Find those classes in split packages that are not 1098*333d2b36SAndroid Build Coastguard Worker # part of an API, i.e. are an internal implementation class, and so 1099*333d2b36SAndroid Build Coastguard Worker # can, and should, be safely moved out of the split packages. 1100*333d2b36SAndroid Build Coastguard Worker 1101*333d2b36SAndroid Build Coastguard Worker split_packages = result.split_packages.keys() 1102*333d2b36SAndroid Build Coastguard Worker result.property_changes.append( 1103*333d2b36SAndroid Build Coastguard Worker HiddenApiPropertyChange( 1104*333d2b36SAndroid Build Coastguard Worker property_name="split_packages", 1105*333d2b36SAndroid Build Coastguard Worker values=split_packages, 1106*333d2b36SAndroid Build Coastguard Worker property_comment=self.split_package_comment(split_packages), 1107*333d2b36SAndroid Build Coastguard Worker action=PropertyChangeAction.REPLACE, 1108*333d2b36SAndroid Build Coastguard Worker )) 1109*333d2b36SAndroid Build Coastguard Worker 1110*333d2b36SAndroid Build Coastguard Worker if split_packages: 1111*333d2b36SAndroid Build Coastguard Worker self.report_dedent(f""" 1112*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment {self.bcpf} contains classes in packages 1113*333d2b36SAndroid Build Coastguard Worker that also contain classes provided by other bootclasspath 1114*333d2b36SAndroid Build Coastguard Worker modules. Those packages are called split packages. Split 1115*333d2b36SAndroid Build Coastguard Worker packages should be avoided where possible but are often 1116*333d2b36SAndroid Build Coastguard Worker unavoidable when modularizing existing code. 1117*333d2b36SAndroid Build Coastguard Worker 1118*333d2b36SAndroid Build Coastguard Worker The hidden api processing needs to know which packages are split 1119*333d2b36SAndroid Build Coastguard Worker (and conversely which are not) so that it can optimize the 1120*333d2b36SAndroid Build Coastguard Worker hidden API flags to remove unnecessary implementation details. 1121*333d2b36SAndroid Build Coastguard Worker 1122*333d2b36SAndroid Build Coastguard Worker By default (for backwards compatibility) the 1123*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment assumes that all packages are split 1124*333d2b36SAndroid Build Coastguard Worker unless one of the package_prefixes or split_packages properties 1125*333d2b36SAndroid Build Coastguard Worker are specified. While that is safe it is not optimal and can lead 1126*333d2b36SAndroid Build Coastguard Worker to unnecessary implementation details leaking into the hidden 1127*333d2b36SAndroid Build Coastguard Worker API flags. Adding an empty split_packages property allows the 1128*333d2b36SAndroid Build Coastguard Worker flags to be optimized and remove any unnecessary implementation 1129*333d2b36SAndroid Build Coastguard Worker details. 1130*333d2b36SAndroid Build Coastguard Worker """) 1131*333d2b36SAndroid Build Coastguard Worker 1132*333d2b36SAndroid Build Coastguard Worker for package in split_packages: 1133*333d2b36SAndroid Build Coastguard Worker reason = result.split_packages[package] 1134*333d2b36SAndroid Build Coastguard Worker self.report(f""" 1135*333d2b36SAndroid Build Coastguard Worker Package {package} is split because while this bootclasspath_fragment 1136*333d2b36SAndroid Build Coastguard Worker provides the following classes: 1137*333d2b36SAndroid Build Coastguard Worker{indent_lines(reason.bcpf)} 1138*333d2b36SAndroid Build Coastguard Worker 1139*333d2b36SAndroid Build Coastguard Worker Other module(s) on the bootclasspath provides the following classes in 1140*333d2b36SAndroid Build Coastguard Worker that package: 1141*333d2b36SAndroid Build Coastguard Worker{indent_lines(reason.other)} 1142*333d2b36SAndroid Build Coastguard Worker""") 1143*333d2b36SAndroid Build Coastguard Worker 1144*333d2b36SAndroid Build Coastguard Worker single_packages = result.single_packages.keys() 1145*333d2b36SAndroid Build Coastguard Worker if single_packages: 1146*333d2b36SAndroid Build Coastguard Worker result.property_changes.append( 1147*333d2b36SAndroid Build Coastguard Worker HiddenApiPropertyChange( 1148*333d2b36SAndroid Build Coastguard Worker property_name="single_packages", 1149*333d2b36SAndroid Build Coastguard Worker values=single_packages, 1150*333d2b36SAndroid Build Coastguard Worker property_comment=textwrap.dedent(""" 1151*333d2b36SAndroid Build Coastguard Worker The following packages currently only contain classes from 1152*333d2b36SAndroid Build Coastguard Worker this bootclasspath_fragment but some of their sub-packages 1153*333d2b36SAndroid Build Coastguard Worker contain classes from other bootclasspath modules. Packages 1154*333d2b36SAndroid Build Coastguard Worker should only be listed here when necessary for legacy 1155*333d2b36SAndroid Build Coastguard Worker purposes, new packages should match a package prefix. 1156*333d2b36SAndroid Build Coastguard Worker """), 1157*333d2b36SAndroid Build Coastguard Worker action=PropertyChangeAction.REPLACE, 1158*333d2b36SAndroid Build Coastguard Worker )) 1159*333d2b36SAndroid Build Coastguard Worker 1160*333d2b36SAndroid Build Coastguard Worker self.report_dedent(f""" 1161*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment {self.bcpf} contains classes from 1162*333d2b36SAndroid Build Coastguard Worker packages that has sub-packages which contain classes provided by 1163*333d2b36SAndroid Build Coastguard Worker other bootclasspath modules. Those packages are called single 1164*333d2b36SAndroid Build Coastguard Worker packages. Single packages should be avoided where possible but 1165*333d2b36SAndroid Build Coastguard Worker are often unavoidable when modularizing existing code. 1166*333d2b36SAndroid Build Coastguard Worker 1167*333d2b36SAndroid Build Coastguard Worker Because some sub-packages contains classes from other 1168*333d2b36SAndroid Build Coastguard Worker bootclasspath modules it is not possible to use the package as a 1169*333d2b36SAndroid Build Coastguard Worker package prefix as that treats the package and all its 1170*333d2b36SAndroid Build Coastguard Worker sub-packages as being provided by this module. 1171*333d2b36SAndroid Build Coastguard Worker """) 1172*333d2b36SAndroid Build Coastguard Worker for package in single_packages: 1173*333d2b36SAndroid Build Coastguard Worker reason = result.single_packages[package] 1174*333d2b36SAndroid Build Coastguard Worker self.report(f""" 1175*333d2b36SAndroid Build Coastguard Worker Package {package} is not a package prefix because while this 1176*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment provides the following sub-packages: 1177*333d2b36SAndroid Build Coastguard Worker{indent_lines(reason.bcpf)} 1178*333d2b36SAndroid Build Coastguard Worker 1179*333d2b36SAndroid Build Coastguard Worker Other module(s) on the bootclasspath provide the following sub-packages: 1180*333d2b36SAndroid Build Coastguard Worker{indent_lines(reason.other)} 1181*333d2b36SAndroid Build Coastguard Worker""") 1182*333d2b36SAndroid Build Coastguard Worker 1183*333d2b36SAndroid Build Coastguard Worker package_prefixes = result.package_prefixes 1184*333d2b36SAndroid Build Coastguard Worker if package_prefixes: 1185*333d2b36SAndroid Build Coastguard Worker result.property_changes.append( 1186*333d2b36SAndroid Build Coastguard Worker HiddenApiPropertyChange( 1187*333d2b36SAndroid Build Coastguard Worker property_name="package_prefixes", 1188*333d2b36SAndroid Build Coastguard Worker values=package_prefixes, 1189*333d2b36SAndroid Build Coastguard Worker property_comment=self.package_prefixes_comment(), 1190*333d2b36SAndroid Build Coastguard Worker action=PropertyChangeAction.REPLACE, 1191*333d2b36SAndroid Build Coastguard Worker )) 1192*333d2b36SAndroid Build Coastguard Worker 1193*333d2b36SAndroid Build Coastguard Worker def explain_how_to_check_signature_patterns(self): 1194*333d2b36SAndroid Build Coastguard Worker signature_patterns_files = self.find_bootclasspath_fragment_output_file( 1195*333d2b36SAndroid Build Coastguard Worker "signature-patterns.csv", required=False) 1196*333d2b36SAndroid Build Coastguard Worker if signature_patterns_files: 1197*333d2b36SAndroid Build Coastguard Worker signature_patterns_files = signature_patterns_files.removeprefix( 1198*333d2b36SAndroid Build Coastguard Worker self.top_dir) 1199*333d2b36SAndroid Build Coastguard Worker 1200*333d2b36SAndroid Build Coastguard Worker self.report_dedent(f""" 1201*333d2b36SAndroid Build Coastguard Worker The purpose of the hiddenapi split_packages and package_prefixes 1202*333d2b36SAndroid Build Coastguard Worker properties is to allow the removal of implementation details 1203*333d2b36SAndroid Build Coastguard Worker from the hidden API flags to reduce the coupling between sdk 1204*333d2b36SAndroid Build Coastguard Worker snapshots and the APEX runtime. It cannot eliminate that 1205*333d2b36SAndroid Build Coastguard Worker coupling completely though. Doing so may require changes to the 1206*333d2b36SAndroid Build Coastguard Worker code. 1207*333d2b36SAndroid Build Coastguard Worker 1208*333d2b36SAndroid Build Coastguard Worker This tool provides support for managing those properties but it 1209*333d2b36SAndroid Build Coastguard Worker cannot decide whether the set of package prefixes suggested is 1210*333d2b36SAndroid Build Coastguard Worker appropriate that needs the input of the developer. 1211*333d2b36SAndroid Build Coastguard Worker 1212*333d2b36SAndroid Build Coastguard Worker Please run the following command: 1213*333d2b36SAndroid Build Coastguard Worker m {signature_patterns_files} 1214*333d2b36SAndroid Build Coastguard Worker 1215*333d2b36SAndroid Build Coastguard Worker And then check the '{signature_patterns_files}' for any mention 1216*333d2b36SAndroid Build Coastguard Worker of implementation classes and packages (i.e. those 1217*333d2b36SAndroid Build Coastguard Worker classes/packages that do not contain any part of an API surface, 1218*333d2b36SAndroid Build Coastguard Worker including the hidden API). If they are found then the code 1219*333d2b36SAndroid Build Coastguard Worker should ideally be moved to a package unique to this module that 1220*333d2b36SAndroid Build Coastguard Worker is contained within a package that is part of an API surface. 1221*333d2b36SAndroid Build Coastguard Worker 1222*333d2b36SAndroid Build Coastguard Worker The format of the file is a list of patterns: 1223*333d2b36SAndroid Build Coastguard Worker 1224*333d2b36SAndroid Build Coastguard Worker * Patterns for split packages will list every class in that package. 1225*333d2b36SAndroid Build Coastguard Worker 1226*333d2b36SAndroid Build Coastguard Worker * Patterns for package prefixes will end with .../**. 1227*333d2b36SAndroid Build Coastguard Worker 1228*333d2b36SAndroid Build Coastguard Worker * Patterns for packages which are not split but cannot use a 1229*333d2b36SAndroid Build Coastguard Worker package prefix because there are sub-packages which are provided 1230*333d2b36SAndroid Build Coastguard Worker by another module will end with .../*. 1231*333d2b36SAndroid Build Coastguard Worker """) 1232*333d2b36SAndroid Build Coastguard Worker 1233*333d2b36SAndroid Build Coastguard Worker def compute_hiddenapi_package_properties(self, result): 1234*333d2b36SAndroid Build Coastguard Worker trie = signature_trie() 1235*333d2b36SAndroid Build Coastguard Worker # Populate the trie with the classes that are provided by the 1236*333d2b36SAndroid Build Coastguard Worker # bootclasspath_fragment tagging them to make it clear where they 1237*333d2b36SAndroid Build Coastguard Worker # are from. 1238*333d2b36SAndroid Build Coastguard Worker sorted_classes = sorted(self.classes) 1239*333d2b36SAndroid Build Coastguard Worker for class_name in sorted_classes: 1240*333d2b36SAndroid Build Coastguard Worker trie.add(class_name + _FAKE_MEMBER, ClassProvider.BCPF) 1241*333d2b36SAndroid Build Coastguard Worker 1242*333d2b36SAndroid Build Coastguard Worker # Now the same for monolithic classes. 1243*333d2b36SAndroid Build Coastguard Worker monolithic_classes = set() 1244*333d2b36SAndroid Build Coastguard Worker abs_flags_file = os.path.join(self.top_dir, _FLAGS_FILE) 1245*333d2b36SAndroid Build Coastguard Worker with open(abs_flags_file, "r", encoding="utf8") as f: 1246*333d2b36SAndroid Build Coastguard Worker for line in iter(f.readline, ""): 1247*333d2b36SAndroid Build Coastguard Worker signature = self.line_to_signature(line) 1248*333d2b36SAndroid Build Coastguard Worker class_name = self.signature_to_class(signature) 1249*333d2b36SAndroid Build Coastguard Worker if (class_name not in monolithic_classes and 1250*333d2b36SAndroid Build Coastguard Worker class_name not in self.classes): 1251*333d2b36SAndroid Build Coastguard Worker trie.add( 1252*333d2b36SAndroid Build Coastguard Worker class_name + _FAKE_MEMBER, 1253*333d2b36SAndroid Build Coastguard Worker ClassProvider.OTHER, 1254*333d2b36SAndroid Build Coastguard Worker only_if_matches=True) 1255*333d2b36SAndroid Build Coastguard Worker monolithic_classes.add(class_name) 1256*333d2b36SAndroid Build Coastguard Worker 1257*333d2b36SAndroid Build Coastguard Worker self.recurse_hiddenapi_packages_trie(trie, result) 1258*333d2b36SAndroid Build Coastguard Worker 1259*333d2b36SAndroid Build Coastguard Worker @staticmethod 1260*333d2b36SAndroid Build Coastguard Worker def selector_to_java_reference(node): 1261*333d2b36SAndroid Build Coastguard Worker return node.selector.replace("/", ".") 1262*333d2b36SAndroid Build Coastguard Worker 1263*333d2b36SAndroid Build Coastguard Worker @staticmethod 1264*333d2b36SAndroid Build Coastguard Worker def determine_reason_for_single_package(node): 1265*333d2b36SAndroid Build Coastguard Worker bcpf_packages = [] 1266*333d2b36SAndroid Build Coastguard Worker other_packages = [] 1267*333d2b36SAndroid Build Coastguard Worker 1268*333d2b36SAndroid Build Coastguard Worker def recurse(n): 1269*333d2b36SAndroid Build Coastguard Worker if n.type != "package": 1270*333d2b36SAndroid Build Coastguard Worker return 1271*333d2b36SAndroid Build Coastguard Worker 1272*333d2b36SAndroid Build Coastguard Worker providers = n.get_matching_rows("*") 1273*333d2b36SAndroid Build Coastguard Worker package_ref = BcpfAnalyzer.selector_to_java_reference(n) 1274*333d2b36SAndroid Build Coastguard Worker if ClassProvider.BCPF in providers: 1275*333d2b36SAndroid Build Coastguard Worker bcpf_packages.append(package_ref) 1276*333d2b36SAndroid Build Coastguard Worker else: 1277*333d2b36SAndroid Build Coastguard Worker other_packages.append(package_ref) 1278*333d2b36SAndroid Build Coastguard Worker 1279*333d2b36SAndroid Build Coastguard Worker children = n.child_nodes() 1280*333d2b36SAndroid Build Coastguard Worker if children: 1281*333d2b36SAndroid Build Coastguard Worker for child in children: 1282*333d2b36SAndroid Build Coastguard Worker recurse(child) 1283*333d2b36SAndroid Build Coastguard Worker 1284*333d2b36SAndroid Build Coastguard Worker recurse(node) 1285*333d2b36SAndroid Build Coastguard Worker return PackagePropertyReason(bcpf=bcpf_packages, other=other_packages) 1286*333d2b36SAndroid Build Coastguard Worker 1287*333d2b36SAndroid Build Coastguard Worker @staticmethod 1288*333d2b36SAndroid Build Coastguard Worker def determine_reason_for_split_package(node): 1289*333d2b36SAndroid Build Coastguard Worker bcpf_classes = [] 1290*333d2b36SAndroid Build Coastguard Worker other_classes = [] 1291*333d2b36SAndroid Build Coastguard Worker for child in node.child_nodes(): 1292*333d2b36SAndroid Build Coastguard Worker if child.type != "class": 1293*333d2b36SAndroid Build Coastguard Worker continue 1294*333d2b36SAndroid Build Coastguard Worker 1295*333d2b36SAndroid Build Coastguard Worker providers = child.values(lambda _: True) 1296*333d2b36SAndroid Build Coastguard Worker class_ref = BcpfAnalyzer.selector_to_java_reference(child) 1297*333d2b36SAndroid Build Coastguard Worker if ClassProvider.BCPF in providers: 1298*333d2b36SAndroid Build Coastguard Worker bcpf_classes.append(class_ref) 1299*333d2b36SAndroid Build Coastguard Worker else: 1300*333d2b36SAndroid Build Coastguard Worker other_classes.append(class_ref) 1301*333d2b36SAndroid Build Coastguard Worker 1302*333d2b36SAndroid Build Coastguard Worker return PackagePropertyReason(bcpf=bcpf_classes, other=other_classes) 1303*333d2b36SAndroid Build Coastguard Worker 1304*333d2b36SAndroid Build Coastguard Worker def recurse_hiddenapi_packages_trie(self, node, result): 1305*333d2b36SAndroid Build Coastguard Worker nodes = node.child_nodes() 1306*333d2b36SAndroid Build Coastguard Worker if nodes: 1307*333d2b36SAndroid Build Coastguard Worker for child in nodes: 1308*333d2b36SAndroid Build Coastguard Worker # Ignore any non-package nodes. 1309*333d2b36SAndroid Build Coastguard Worker if child.type != "package": 1310*333d2b36SAndroid Build Coastguard Worker continue 1311*333d2b36SAndroid Build Coastguard Worker 1312*333d2b36SAndroid Build Coastguard Worker package = self.selector_to_java_reference(child) 1313*333d2b36SAndroid Build Coastguard Worker 1314*333d2b36SAndroid Build Coastguard Worker providers = set(child.get_matching_rows("**")) 1315*333d2b36SAndroid Build Coastguard Worker if not providers: 1316*333d2b36SAndroid Build Coastguard Worker # The package and all its sub packages contain no 1317*333d2b36SAndroid Build Coastguard Worker # classes. This should never happen. 1318*333d2b36SAndroid Build Coastguard Worker pass 1319*333d2b36SAndroid Build Coastguard Worker elif providers == {ClassProvider.BCPF}: 1320*333d2b36SAndroid Build Coastguard Worker # The package and all its sub packages only contain 1321*333d2b36SAndroid Build Coastguard Worker # classes provided by the bootclasspath_fragment. 1322*333d2b36SAndroid Build Coastguard Worker logging.debug("Package '%s.**' is not split", package) 1323*333d2b36SAndroid Build Coastguard Worker result.package_prefixes.append(package) 1324*333d2b36SAndroid Build Coastguard Worker # There is no point traversing into the sub packages. 1325*333d2b36SAndroid Build Coastguard Worker continue 1326*333d2b36SAndroid Build Coastguard Worker elif providers == {ClassProvider.OTHER}: 1327*333d2b36SAndroid Build Coastguard Worker # The package and all its sub packages contain no 1328*333d2b36SAndroid Build Coastguard Worker # classes provided by the bootclasspath_fragment. 1329*333d2b36SAndroid Build Coastguard Worker # There is no point traversing into the sub packages. 1330*333d2b36SAndroid Build Coastguard Worker logging.debug("Package '%s.**' contains no classes from %s", 1331*333d2b36SAndroid Build Coastguard Worker package, self.bcpf) 1332*333d2b36SAndroid Build Coastguard Worker continue 1333*333d2b36SAndroid Build Coastguard Worker elif ClassProvider.BCPF in providers: 1334*333d2b36SAndroid Build Coastguard Worker # The package and all its sub packages contain classes 1335*333d2b36SAndroid Build Coastguard Worker # provided by the bootclasspath_fragment and other 1336*333d2b36SAndroid Build Coastguard Worker # sources. 1337*333d2b36SAndroid Build Coastguard Worker logging.debug( 1338*333d2b36SAndroid Build Coastguard Worker "Package '%s.**' contains classes from " 1339*333d2b36SAndroid Build Coastguard Worker "%s and other sources", package, self.bcpf) 1340*333d2b36SAndroid Build Coastguard Worker 1341*333d2b36SAndroid Build Coastguard Worker providers = set(child.get_matching_rows("*")) 1342*333d2b36SAndroid Build Coastguard Worker if not providers: 1343*333d2b36SAndroid Build Coastguard Worker # The package contains no classes. 1344*333d2b36SAndroid Build Coastguard Worker logging.debug("Package: %s contains no classes", package) 1345*333d2b36SAndroid Build Coastguard Worker elif providers == {ClassProvider.BCPF}: 1346*333d2b36SAndroid Build Coastguard Worker # The package only contains classes provided by the 1347*333d2b36SAndroid Build Coastguard Worker # bootclasspath_fragment. 1348*333d2b36SAndroid Build Coastguard Worker logging.debug( 1349*333d2b36SAndroid Build Coastguard Worker "Package '%s.*' is not split but does have " 1350*333d2b36SAndroid Build Coastguard Worker "sub-packages from other modules", package) 1351*333d2b36SAndroid Build Coastguard Worker 1352*333d2b36SAndroid Build Coastguard Worker # Partition the sub-packages into those that are provided by 1353*333d2b36SAndroid Build Coastguard Worker # this bootclasspath_fragment and those provided by other 1354*333d2b36SAndroid Build Coastguard Worker # modules. They can be used to explain the reason for the 1355*333d2b36SAndroid Build Coastguard Worker # single package to developers. 1356*333d2b36SAndroid Build Coastguard Worker reason = self.determine_reason_for_single_package(child) 1357*333d2b36SAndroid Build Coastguard Worker result.single_packages[package] = reason 1358*333d2b36SAndroid Build Coastguard Worker 1359*333d2b36SAndroid Build Coastguard Worker elif providers == {ClassProvider.OTHER}: 1360*333d2b36SAndroid Build Coastguard Worker # The package contains no classes provided by the 1361*333d2b36SAndroid Build Coastguard Worker # bootclasspath_fragment. Child nodes make contain such 1362*333d2b36SAndroid Build Coastguard Worker # classes. 1363*333d2b36SAndroid Build Coastguard Worker logging.debug("Package '%s.*' contains no classes from %s", 1364*333d2b36SAndroid Build Coastguard Worker package, self.bcpf) 1365*333d2b36SAndroid Build Coastguard Worker elif ClassProvider.BCPF in providers: 1366*333d2b36SAndroid Build Coastguard Worker # The package contains classes provided by both the 1367*333d2b36SAndroid Build Coastguard Worker # bootclasspath_fragment and some other source. 1368*333d2b36SAndroid Build Coastguard Worker logging.debug("Package '%s.*' is split", package) 1369*333d2b36SAndroid Build Coastguard Worker 1370*333d2b36SAndroid Build Coastguard Worker # Partition the classes in this split package into those 1371*333d2b36SAndroid Build Coastguard Worker # that come from this bootclasspath_fragment and those that 1372*333d2b36SAndroid Build Coastguard Worker # come from other modules. That can be used to explain the 1373*333d2b36SAndroid Build Coastguard Worker # reason for the split package to developers. 1374*333d2b36SAndroid Build Coastguard Worker reason = self.determine_reason_for_split_package(child) 1375*333d2b36SAndroid Build Coastguard Worker result.split_packages[package] = reason 1376*333d2b36SAndroid Build Coastguard Worker 1377*333d2b36SAndroid Build Coastguard Worker self.recurse_hiddenapi_packages_trie(child, result) 1378*333d2b36SAndroid Build Coastguard Worker 1379*333d2b36SAndroid Build Coastguard Worker 1380*333d2b36SAndroid Build Coastguard Workerdef newline_stripping_iter(iterator): 1381*333d2b36SAndroid Build Coastguard Worker """Return an iterator over the iterator that strips trailing white space.""" 1382*333d2b36SAndroid Build Coastguard Worker lines = iter(iterator, "") 1383*333d2b36SAndroid Build Coastguard Worker lines = (line.rstrip() for line in lines) 1384*333d2b36SAndroid Build Coastguard Worker return lines 1385*333d2b36SAndroid Build Coastguard Worker 1386*333d2b36SAndroid Build Coastguard Worker 1387*333d2b36SAndroid Build Coastguard Workerdef format_comment_as_text(text, indent): 1388*333d2b36SAndroid Build Coastguard Worker return "".join( 1389*333d2b36SAndroid Build Coastguard Worker [f"{line}\n" for line in format_comment_as_lines(text, indent)]) 1390*333d2b36SAndroid Build Coastguard Worker 1391*333d2b36SAndroid Build Coastguard Worker 1392*333d2b36SAndroid Build Coastguard Workerdef format_comment_as_lines(text, indent): 1393*333d2b36SAndroid Build Coastguard Worker lines = textwrap.wrap(text.strip("\n"), width=77 - len(indent)) 1394*333d2b36SAndroid Build Coastguard Worker lines = [f"{indent}// {line}" for line in lines] 1395*333d2b36SAndroid Build Coastguard Worker return lines 1396*333d2b36SAndroid Build Coastguard Worker 1397*333d2b36SAndroid Build Coastguard Worker 1398*333d2b36SAndroid Build Coastguard Workerdef log_stream_for_subprocess(): 1399*333d2b36SAndroid Build Coastguard Worker stream = subprocess.DEVNULL 1400*333d2b36SAndroid Build Coastguard Worker for handler in logging.root.handlers: 1401*333d2b36SAndroid Build Coastguard Worker if handler.level == logging.DEBUG: 1402*333d2b36SAndroid Build Coastguard Worker if isinstance(handler, logging.StreamHandler): 1403*333d2b36SAndroid Build Coastguard Worker stream = handler.stream 1404*333d2b36SAndroid Build Coastguard Worker return stream 1405*333d2b36SAndroid Build Coastguard Worker 1406*333d2b36SAndroid Build Coastguard Worker 1407*333d2b36SAndroid Build Coastguard Workerdef main(argv): 1408*333d2b36SAndroid Build Coastguard Worker args_parser = argparse.ArgumentParser( 1409*333d2b36SAndroid Build Coastguard Worker description="Analyze a bootclasspath_fragment module.") 1410*333d2b36SAndroid Build Coastguard Worker args_parser.add_argument( 1411*333d2b36SAndroid Build Coastguard Worker "--bcpf", 1412*333d2b36SAndroid Build Coastguard Worker help="The bootclasspath_fragment module to analyze", 1413*333d2b36SAndroid Build Coastguard Worker required=True, 1414*333d2b36SAndroid Build Coastguard Worker ) 1415*333d2b36SAndroid Build Coastguard Worker args_parser.add_argument( 1416*333d2b36SAndroid Build Coastguard Worker "--apex", 1417*333d2b36SAndroid Build Coastguard Worker help="The apex module to which the bootclasspath_fragment belongs. It " 1418*333d2b36SAndroid Build Coastguard Worker "is not strictly necessary at the moment but providing it will " 1419*333d2b36SAndroid Build Coastguard Worker "allow this script to give more useful messages and it may be" 1420*333d2b36SAndroid Build Coastguard Worker "required in future.", 1421*333d2b36SAndroid Build Coastguard Worker default="SPECIFY-APEX-OPTION") 1422*333d2b36SAndroid Build Coastguard Worker args_parser.add_argument( 1423*333d2b36SAndroid Build Coastguard Worker "--sdk", 1424*333d2b36SAndroid Build Coastguard Worker help="The sdk module to which the bootclasspath_fragment belongs. It " 1425*333d2b36SAndroid Build Coastguard Worker "is not strictly necessary at the moment but providing it will " 1426*333d2b36SAndroid Build Coastguard Worker "allow this script to give more useful messages and it may be" 1427*333d2b36SAndroid Build Coastguard Worker "required in future.", 1428*333d2b36SAndroid Build Coastguard Worker default="SPECIFY-SDK-OPTION") 1429*333d2b36SAndroid Build Coastguard Worker args_parser.add_argument( 1430*333d2b36SAndroid Build Coastguard Worker "--fix", 1431*333d2b36SAndroid Build Coastguard Worker help="Attempt to fix any issues found automatically.", 1432*333d2b36SAndroid Build Coastguard Worker action="store_true", 1433*333d2b36SAndroid Build Coastguard Worker default=False) 1434*333d2b36SAndroid Build Coastguard Worker args = args_parser.parse_args(argv[1:]) 1435*333d2b36SAndroid Build Coastguard Worker top_dir = os.environ["ANDROID_BUILD_TOP"] + "/" 1436*333d2b36SAndroid Build Coastguard Worker out_dir = os.environ.get("OUT_DIR", os.path.join(top_dir, "out")) 1437*333d2b36SAndroid Build Coastguard Worker product_out_dir = os.environ.get("ANDROID_PRODUCT_OUT", top_dir) 1438*333d2b36SAndroid Build Coastguard Worker # Make product_out_dir relative to the top so it can be used as part of a 1439*333d2b36SAndroid Build Coastguard Worker # build target. 1440*333d2b36SAndroid Build Coastguard Worker product_out_dir = product_out_dir.removeprefix(top_dir) 1441*333d2b36SAndroid Build Coastguard Worker log_fd, abs_log_file = tempfile.mkstemp( 1442*333d2b36SAndroid Build Coastguard Worker suffix="_analyze_bcpf.log", text=True) 1443*333d2b36SAndroid Build Coastguard Worker 1444*333d2b36SAndroid Build Coastguard Worker with os.fdopen(log_fd, "w") as log_file: 1445*333d2b36SAndroid Build Coastguard Worker # Set up debug logging to the log file. 1446*333d2b36SAndroid Build Coastguard Worker logging.basicConfig( 1447*333d2b36SAndroid Build Coastguard Worker level=logging.DEBUG, 1448*333d2b36SAndroid Build Coastguard Worker format="%(levelname)-8s %(message)s", 1449*333d2b36SAndroid Build Coastguard Worker stream=log_file) 1450*333d2b36SAndroid Build Coastguard Worker 1451*333d2b36SAndroid Build Coastguard Worker # define a Handler which writes INFO messages or higher to the 1452*333d2b36SAndroid Build Coastguard Worker # sys.stdout with just the message. 1453*333d2b36SAndroid Build Coastguard Worker console = logging.StreamHandler() 1454*333d2b36SAndroid Build Coastguard Worker console.setLevel(logging.INFO) 1455*333d2b36SAndroid Build Coastguard Worker console.setFormatter(logging.Formatter("%(message)s")) 1456*333d2b36SAndroid Build Coastguard Worker # add the handler to the root logger 1457*333d2b36SAndroid Build Coastguard Worker logging.getLogger("").addHandler(console) 1458*333d2b36SAndroid Build Coastguard Worker 1459*333d2b36SAndroid Build Coastguard Worker print(f"Writing log to {abs_log_file}") 1460*333d2b36SAndroid Build Coastguard Worker try: 1461*333d2b36SAndroid Build Coastguard Worker analyzer = BcpfAnalyzer( 1462*333d2b36SAndroid Build Coastguard Worker tool_path=argv[0], 1463*333d2b36SAndroid Build Coastguard Worker top_dir=top_dir, 1464*333d2b36SAndroid Build Coastguard Worker out_dir=out_dir, 1465*333d2b36SAndroid Build Coastguard Worker product_out_dir=product_out_dir, 1466*333d2b36SAndroid Build Coastguard Worker bcpf=args.bcpf, 1467*333d2b36SAndroid Build Coastguard Worker apex=args.apex, 1468*333d2b36SAndroid Build Coastguard Worker sdk=args.sdk, 1469*333d2b36SAndroid Build Coastguard Worker fix=args.fix, 1470*333d2b36SAndroid Build Coastguard Worker ) 1471*333d2b36SAndroid Build Coastguard Worker analyzer.analyze() 1472*333d2b36SAndroid Build Coastguard Worker finally: 1473*333d2b36SAndroid Build Coastguard Worker print(f"Log written to {abs_log_file}") 1474*333d2b36SAndroid Build Coastguard Worker 1475*333d2b36SAndroid Build Coastguard Worker 1476*333d2b36SAndroid Build Coastguard Workerif __name__ == "__main__": 1477*333d2b36SAndroid Build Coastguard Worker main(sys.argv) 1478