analyze_bcpf: Indent multi-line text to improve formatting

Addresses a comment on the review of the initial changes to add
analyze_bcpf script.

Bug: 202154151
Test: m analyze_bcpf && analyze_bcpf --bcpf art-bootclasspath-fragment
      m analyze_bcpf && analyze_bcpf --bcpf art-bootclasspath-fragment --fix
      atest --host analyze_bcpf_test
Change-Id: I8c030dcc1a29f106ca57dca8c97a5cb4425e7674
diff --git a/scripts/hiddenapi/analyze_bcpf.py b/scripts/hiddenapi/analyze_bcpf.py
index 1ad8d07..9e8807e 100644
--- a/scripts/hiddenapi/analyze_bcpf.py
+++ b/scripts/hiddenapi/analyze_bcpf.py
@@ -394,13 +394,17 @@
     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 @@
     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 @@
         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 @@
 
         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 @@
         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 @@
 
             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 @@
         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 @@
         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 @@
             ))
 
         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 @@
                     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 @@
             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()