diff --git a/scripts/hiddenapi/analyze_bcpf.py b/scripts/hiddenapi/analyze_bcpf.py index 1ad8d07e9..9e8807eaf 100644 --- a/scripts/hiddenapi/analyze_bcpf.py +++ b/scripts/hiddenapi/analyze_bcpf.py @@ -394,13 +394,17 @@ class BcpfAnalyzer: def reformat_report_test(text): return re.sub(r"(.)\n([^\s])", r"\1 \2", text) - def report(self, text, **kwargs): + def report(self, text="", **kwargs): # Concatenate lines that are not separated by a blank line together to # eliminate formatting applied to the supplied text to adhere to python # line length limitations. text = self.reformat_report_test(text) logging.info("%s", text, **kwargs) + def report_dedent(self, text, **kwargs): + text = textwrap.dedent(text) + self.report(text, **kwargs) + def run_command(self, cmd, *args, **kwargs): cmd_line = " ".join(cmd) logging.debug("Running %s", cmd_line) @@ -442,9 +446,7 @@ class BcpfAnalyzer: def load_module_info(self): module_info_file = os.path.join(self.product_out_dir, "module-info.json") - self.report(f""" -Making sure that {module_info_file} is up to date. -""") + self.report(f"\nMaking sure that {module_info_file} is up to date.\n") output = self.build_file_read_output(module_info_file) lines = output.lines() for line in lines: @@ -496,61 +498,62 @@ Making sure that {module_info_file} is up to date. optimizations that can be applied. """ self.report(f"Analyzing bootclasspath_fragment module {self.bcpf}") - self.report(f""" -Run this tool to help initialize a bootclasspath_fragment module. Before you -start make sure that: + self.report_dedent(f""" + Run this tool to help initialize a bootclasspath_fragment module. + Before you start make sure that: -1. The current checkout is up to date. + 1. The current checkout is up to date. -2. The environment has been initialized using lunch, e.g. - lunch aosp_arm64-userdebug + 2. The environment has been initialized using lunch, e.g. + lunch aosp_arm64-userdebug -3. You have added a bootclasspath_fragment module to the appropriate Android.bp -file. Something like this: + 3. You have added a bootclasspath_fragment module to the appropriate + Android.bp file. Something like this: - bootclasspath_fragment {{ - name: "{self.bcpf}", - contents: [ - "...", - ], + bootclasspath_fragment {{ + name: "{self.bcpf}", + contents: [ + "...", + ], + + // The bootclasspath_fragments that provide APIs on which this + // depends. + fragments: [ + {{ + apex: "com.android.art", + module: "art-bootclasspath-fragment", + }}, + ], + }} + + 4. You have added it to the platform_bootclasspath module in + frameworks/base/boot/Android.bp. Something like this: - // The bootclasspath_fragments that provide APIs on which this depends. - fragments: [ - {{ - apex: "com.android.art", - module: "art-bootclasspath-fragment", - }}, - ], - }} + platform_bootclasspath {{ + name: "platform-bootclasspath", + fragments: [ + ... + {{ + apex: "{self.apex}", + module: "{self.bcpf}", + }}, + ], + }} -4. You have added it to the platform_bootclasspath module in -frameworks/base/boot/Android.bp. Something like this: + 5. You have added an sdk module. Something like this: - platform_bootclasspath {{ - name: "platform-bootclasspath", - fragments: [ - ... - {{ - apex: "{self.apex}", - module: "{self.bcpf}", - }}, - ], - }} - -5. You have added an sdk module. Something like this: - - sdk {{ - name: "{self.sdk}", - bootclasspath_fragments: ["{self.bcpf}"], - }} -""") + sdk {{ + name: "{self.sdk}", + bootclasspath_fragments: ["{self.bcpf}"], + }} + """) # Make sure that the module-info.json file is up to date. self.load_module_info() - self.report(""" -Cleaning potentially stale files. -""") + self.report_dedent(""" + Cleaning potentially stale files. + """) # Remove the out/soong/hiddenapi files. shutil.rmtree(f"{self.out_dir}/soong/hiddenapi", ignore_errors=True) @@ -605,26 +608,26 @@ merge these properties into it. if result.file_changes: if self.fix: - file_change_message = """ -The following files were modified by this script:""" + file_change_message = textwrap.dedent(""" + The following files were modified by this script: + """) else: - file_change_message = """ -The following modifications need to be made:""" + file_change_message = textwrap.dedent(""" + The following modifications need to be made: + """) - self.report(f""" -{file_change_message}""") + self.report(file_change_message) result.file_changes.sort() for file_change in result.file_changes: - self.report(f""" - {file_change.path} - {file_change.description} -""".lstrip("\n")) + self.report(f" {file_change.path}") + self.report(f" {file_change.description}") + self.report() if not self.fix: - self.report(""" -Run the command again with the --fix option to automatically make the above -changes. -""".lstrip()) + self.report_dedent(""" + Run the command again with the --fix option to automatically + make the above changes. + """.lstrip("\n")) def new_file_change(self, file, description): return FileChange( @@ -635,11 +638,10 @@ changes. if not (module_line.startswith("< ") and monolithic_line.startswith("> ") and not separator_line): # Something went wrong. - self.report(f"""Invalid build output detected: - module_line: "{module_line}" - monolithic_line: "{monolithic_line}" - separator_line: "{separator_line}" -""") + self.report("Invalid build output detected:") + self.report(f" module_line: '{module_line}'") + self.report(f" monolithic_line: '{monolithic_line}'") + self.report(f" separator_line: '{separator_line}'") sys.exit(1) if significant: @@ -698,10 +700,9 @@ changes. if module_signature != monolithic_signature: # Something went wrong. - self.report(f"""Inconsistent signatures detected: - module_signature: "{module_signature}" - monolithic_signature: "{monolithic_signature}" -""") + self.report("Inconsistent signatures detected:") + self.report(f" module_signature: '{module_signature}'") + self.report(f" monolithic_signature: '{monolithic_signature}'") sys.exit(1) diffs[module_signature] = (module_flags, monolithic_flags) @@ -749,85 +750,91 @@ changes. return diffs def build_monolithic_stubs_flags(self): - self.report(f""" -Attempting to build {_STUB_FLAGS_FILE} to verify that the -bootclasspath_fragment has the correct API stubs available... -""") + self.report_dedent(f""" + Attempting to build {_STUB_FLAGS_FILE} to verify that the + bootclasspath_fragment has the correct API stubs available... + """) # Build the hiddenapi-stubs-flags.txt file. diffs = self.build_hiddenapi_flags(_STUB_FLAGS_FILE) if diffs: - self.report(f""" -There is a discrepancy between the stub API derived flags created by the -bootclasspath_fragment and the platform_bootclasspath. See preceding error -messages to see which flags are inconsistent. The inconsistencies can occur for -a couple of reasons: + self.report_dedent(f""" + There is a discrepancy between the stub API derived flags + created by the bootclasspath_fragment and the + platform_bootclasspath. See preceding error messages to see + which flags are inconsistent. The inconsistencies can occur for + a couple of reasons: -If you are building against prebuilts of the Android SDK, e.g. by using -TARGET_BUILD_APPS then the prebuilt versions of the APIs this -bootclasspath_fragment depends upon are out of date and need updating. See -go/update-prebuilts for help. + If you are building against prebuilts of the Android SDK, e.g. + by using TARGET_BUILD_APPS then the prebuilt versions of the + APIs this bootclasspath_fragment depends upon are out of date + and need updating. See go/update-prebuilts for help. -Otherwise, this is happening because there are some stub APIs that are either -provided by or used by the contents of the bootclasspath_fragment but which are -not available to it. There are 4 ways to handle this: + Otherwise, this is happening because there are some stub APIs + that are either provided by or used by the contents of the + bootclasspath_fragment but which are not available to it. There + are 4 ways to handle this: -1. A java_sdk_library in the contents property will automatically make its stub - APIs available to the bootclasspath_fragment so nothing needs to be done. + 1. A java_sdk_library in the contents property will + automatically make its stub APIs available to the + bootclasspath_fragment so nothing needs to be done. -2. If the API provided by the bootclasspath_fragment is created by an api_only - java_sdk_library (or a java_library that compiles files generated by a - separate droidstubs module then it cannot be added to the contents and - instead must be added to the api.stubs property, e.g. + 2. If the API provided by the bootclasspath_fragment is created + by an api_only java_sdk_library (or a java_library that compiles + files generated by a separate droidstubs module then it cannot + be added to the contents and instead must be added to the + api.stubs property, e.g. - bootclasspath_fragment {{ - name: "{self.bcpf}", - ... - api: {{ - stubs: ["$MODULE-api-only"]," - }}, - }} + bootclasspath_fragment {{ + name: "{self.bcpf}", + ... + api: {{ + stubs: ["$MODULE-api-only"]," + }}, + }} -3. If the contents use APIs provided by another bootclasspath_fragment then - it needs to be added to the fragments property, e.g. + 3. If the contents use APIs provided by another + bootclasspath_fragment then it needs to be added to the + fragments property, e.g. + + bootclasspath_fragment {{ + name: "{self.bcpf}", + ... + // The bootclasspath_fragments that provide APIs on which this depends. + fragments: [ + ... + {{ + apex: "com.android.other", + module: "com.android.other-bootclasspath-fragment", + }}, + ], + }} + + 4. If the contents use APIs from a module that is not part of + another bootclasspath_fragment then it must be added to the + additional_stubs property, e.g. - bootclasspath_fragment {{ - name: "{self.bcpf}", - ... - // The bootclasspath_fragments that provide APIs on which this depends. - fragments: [ - ... - {{ - apex: "com.android.other", - module: "com.android.other-bootclasspath-fragment", - }}, - ], - }} + bootclasspath_fragment {{ + name: "{self.bcpf}", + ... + additional_stubs: ["android-non-updatable"], + }} -4. If the contents use APIs from a module that is not part of another - bootclasspath_fragment then it must be added to the additional_stubs - property, e.g. + Like the api.stubs property these are typically + java_sdk_library modules but can be java_library too. - bootclasspath_fragment {{ - name: "{self.bcpf}", - ... - additional_stubs: ["android-non-updatable"], - }} - - Like the api.stubs property these are typically java_sdk_library modules but - can be java_library too. - - Note: The "android-non-updatable" is treated as if it was a java_sdk_library - which it is not at the moment but will be in future. -""") + Note: The "android-non-updatable" is treated as if it was a + java_sdk_library which it is not at the moment but will be in + future. + """) return diffs def build_monolithic_flags(self, result): - self.report(f""" -Attempting to build {_FLAGS_FILE} to verify that the -bootclasspath_fragment has the correct hidden API flags... -""") + self.report_dedent(f""" + Attempting to build {_FLAGS_FILE} to verify that the + bootclasspath_fragment has the correct hidden API flags... + """) # Build the hiddenapi-flags.csv file and extract any differences in # the flags between this bootclasspath_fragment and the monolithic @@ -838,32 +845,34 @@ bootclasspath_fragment has the correct hidden API flags... self.load_all_flags() if result.diffs: - self.report(f""" -There is a discrepancy between the hidden API flags created by the -bootclasspath_fragment and the platform_bootclasspath. See preceding error -messages to see which flags are inconsistent. The inconsistencies can occur for -a couple of reasons: + self.report_dedent(f""" + There is a discrepancy between the hidden API flags created by + the bootclasspath_fragment and the platform_bootclasspath. See + preceding error messages to see which flags are inconsistent. + The inconsistencies can occur for a couple of reasons: -If you are building against prebuilts of this bootclasspath_fragment then the -prebuilt version of the sdk snapshot (specifically the hidden API flag files) -are inconsistent with the prebuilt version of the apex {self.apex}. Please -ensure that they are both updated from the same build. + If you are building against prebuilts of this + bootclasspath_fragment then the prebuilt version of the sdk + snapshot (specifically the hidden API flag files) are + inconsistent with the prebuilt version of the apex {self.apex}. + Please ensure that they are both updated from the same build. -1. There are custom hidden API flags specified in the one of the files in - frameworks/base/boot/hiddenapi which apply to the bootclasspath_fragment but - which are not supplied to the bootclasspath_fragment module. + 1. There are custom hidden API flags specified in the one of the + files in frameworks/base/boot/hiddenapi which apply to the + bootclasspath_fragment but which are not supplied to the + bootclasspath_fragment module. -2. The bootclasspath_fragment specifies invalid "package_prefixes" or - "split_packages" properties that match packages and classes that it does not - provide. - -""") + 2. The bootclasspath_fragment specifies invalid + "split_packages", "single_packages" and/of "package_prefixes" + properties that match packages and classes that it does not + provide. + """) # Check to see if there are any hiddenapi related properties that # need to be added to the - self.report(""" -Checking custom hidden API flags.... -""") + self.report_dedent(""" + Checking custom hidden API flags.... + """) self.check_frameworks_base_boot_hidden_api_files(result) def report_hidden_api_flag_file_changes(self, result, property_name, @@ -1060,25 +1069,26 @@ Checking custom hidden API flags.... )) if split_packages: - self.report(f""" -bootclasspath_fragment {self.bcpf} contains classes in packages that also -contain classes provided by other sources, those packages are called split -packages. Split packages should be avoided where possible but are often -unavoidable when modularizing existing code. + self.report_dedent(f""" + bootclasspath_fragment {self.bcpf} contains classes in packages + that also contain classes provided by other bootclasspath + modules. Those packages are called split packages. Split + packages should be avoided where possible but are often + unavoidable when modularizing existing code. -The hidden api processing needs to know which packages are split (and conversely -which are not) so that it can optimize the hidden API flags to remove -unnecessary implementation details. -""") + The hidden api processing needs to know which packages are split + (and conversely which are not) so that it can optimize the + hidden API flags to remove unnecessary implementation details. - self.report(""" -By default (for backwards compatibility) the bootclasspath_fragment assumes that -all packages are split unless one of the package_prefixes or split_packages -properties are specified. While that is safe it is not optimal and can lead to -unnecessary implementation details leaking into the hidden API flags. Adding an -empty split_packages property allows the flags to be optimized and remove any -unnecessary implementation details. -""") + By default (for backwards compatibility) the + bootclasspath_fragment assumes that all packages are split + unless one of the package_prefixes or split_packages properties + are specified. While that is safe it is not optimal and can lead + to unnecessary implementation details leaking into the hidden + API flags. Adding an empty split_packages property allows the + flags to be optimized and remove any unnecessary implementation + details. + """) if single_packages: result.property_changes.append( @@ -1091,7 +1101,7 @@ unnecessary implementation details. contain classes from other bootclasspath modules. Packages should only be listed here when necessary for legacy purposes, new packages should match a package prefix. - """), + """), action=PropertyChangeAction.REPLACE, )) @@ -1111,36 +1121,38 @@ unnecessary implementation details. signature_patterns_files = signature_patterns_files.removeprefix( self.top_dir) - self.report(f""" -The purpose of the hiddenapi split_packages and package_prefixes properties is -to allow the removal of implementation details from the hidden API flags to -reduce the coupling between sdk snapshots and the APEX runtime. It cannot -eliminate that coupling completely though. Doing so may require changes to the -code. + self.report_dedent(f""" + The purpose of the hiddenapi split_packages and package_prefixes + properties is to allow the removal of implementation details + from the hidden API flags to reduce the coupling between sdk + snapshots and the APEX runtime. It cannot eliminate that + coupling completely though. Doing so may require changes to the + code. -This tool provides support for managing those properties but it cannot decide -whether the set of package prefixes suggested is appropriate that needs the -input of the developer. + This tool provides support for managing those properties but it + cannot decide whether the set of package prefixes suggested is + appropriate that needs the input of the developer. -Please run the following command: - m {signature_patterns_files} + Please run the following command: + m {signature_patterns_files} -And then check the '{signature_patterns_files}' for any mention of -implementation classes and packages (i.e. those classes/packages that do not -contain any part of an API surface, including the hidden API). If they are -found then the code should ideally be moved to a package unique to this module -that is contained within a package that is part of an API surface. + And then check the '{signature_patterns_files}' for any mention + of implementation classes and packages (i.e. those + classes/packages that do not contain any part of an API surface, + including the hidden API). If they are found then the code + should ideally be moved to a package unique to this module that + is contained within a package that is part of an API surface. -The format of the file is a list of patterns: + The format of the file is a list of patterns: -* Patterns for split packages will list every class in that package. + * Patterns for split packages will list every class in that package. -* Patterns for package prefixes will end with .../**. + * Patterns for package prefixes will end with .../**. -* Patterns for packages which are not split but cannot use a package prefix -because there are sub-packages which are provided by another module will end -with .../*. -""") + * Patterns for packages which are not split but cannot use a + package prefix because there are sub-packages which are provided + by another module will end with .../*. + """) def compute_hiddenapi_package_properties(self): trie = signature_trie()