xref: /aosp_15_r20/tools/metalava/scripts/gather-android-metalava-artifacts.py (revision 115816f9299ab6ddd6b9673b81f34e707f6bacab)
1#!/usr/bin/env -S python3 -u
2#  Copyright (C) 2024 The Android Open Source Project
3#
4#  Licensed under the Apache License, Version 2.0 (the "License");
5#  you may not use this file except in compliance with the License.
6#  You may obtain a copy of the License at
7#
8#       http://www.apache.org/licenses/LICENSE-2.0
9#
10#  Unless required by applicable law or agreed to in writing, software
11#  distributed under the License is distributed on an "AS IS" BASIS,
12#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13#  See the License for the specific language governing permissions and
14#  limitations under the License.
15
16import argparse
17import os
18from pathlib import Path
19import shutil
20import subprocess
21import sys
22
23# Formatted using: pyformat -s 4 --force_quote_type double -i scripts/gather-android-metalava-artifacts.py
24
25
26def parse_command_line_args(args):
27    """Define the command line options and the parse the command line arguments with them.
28
29    :param args: the command line arguments :return: Return the result of
30    parsing the command line arguments.
31    """
32    args_parser = argparse.ArgumentParser(
33        description=(
34            "Gather Android artifacts created by Metalava. This will build and"
35            " then copy a set of targets to the output directory. If no custom"
36            " targets are provided then a set of default ones will be provided"
37            " that covers stub generation, signature to JDiff conversion and"
38            " api-versions.xml file generation. The intent is that this would"
39            " be run this before and after making the change to build and copy"
40            " the artifacts into two separate directories that can then be"
41            " compared to see what, if any, changes have happened. This does"
42            " not check signature file generation as that can be easily checked"
43            " by running `m checkapi`."
44        ),
45    )
46    args_parser.add_argument(
47        "directory",
48        help="Output directory into which artifacts will be copied.",
49    )
50    args_parser.add_argument(
51        "--stub-src-jar",
52        action="append",
53        help="Additional stub jar to gather",
54    )
55
56    return args_parser.parse_args(args)
57
58
59def default_stub_files():
60    """:return: A representative sample list of stub source jars generated by the Android build using Metalava"""
61    return [
62        f"out/target/common/docs/{x}-stubs.srcjar"
63        for x in [
64            "api-stubs-docs-non-updatable",
65            "system-api-stubs-docs-non-updatable",
66            "test-api-stubs-docs-non-updatable",
67            "module-lib-api-stubs-docs-non-updatable",
68        ]
69    ]
70
71
72def default_doc_stub_files():
73    """:return: A representative sample list of doc stub source jars generated by the Android build using Metalava"""
74    return [
75        "out/target/common/docs/framework-doc-stubs-stubs.srcjar",
76        "out/target/common/docs/framework-doc-system-stubs-stubs.srcjar",
77    ]
78
79
80def default_api_version_files():
81    """:return: A representative sample list of `api-versions.xml` files generated by the Android build using
82
83    Metalava.
84    """
85    return [
86        "out/soong/lint/api_versions_public.xml",
87        "out/soong/lint/api_versions_system.xml",
88        "out/soong/lint/api_versions_module_lib.xml",
89        "out/soong/lint/api_versions_system_server.xml",
90        "out/target/common/obj/PACKAGING/api_versions_module_lib_complete_generated-api-versions.xml",
91        "out/target/common/obj/PACKAGING/api_versions_system_server_complete_generated-api-versions.xml",
92    ]
93
94
95def default_jdiff_files():
96    """:return: A representative sample list of JDiff files created by the Android build using Metalava."""
97    return [
98        # JDiff files generated from jar files.
99        "out/target/common/obj/api.xml",
100        "out/target/common/obj/system-api.xml",
101        "out/target/common/obj/module-lib-api.xml",
102        "out/target/common/obj/system-server-api.xml",
103        "out/target/common/obj/test-api.xml",
104        # JDiff files generated from txt files.
105        "out/soong/.intermediates/packages/services/Car/car-lib/android.car-test-stubs-jdiff/gen/car-test-api.xml",
106        "out/soong/.intermediates/packages/services/Car/car-lib/android.car-stubs-jdiff/gen/car-api.xml",
107        "out/soong/.intermediates/packages/services/Car/car-lib/android.car-system-stubs-jdiff/gen/car-system-api.xml",
108        ""# JDiff files generated from txt files and then compressed using gzip..
109        "out/soong/.intermediates/cts/tests/signature/api/cts-android-test-base-current-api-gz/gen/android-test-base-current.api.gz",
110        "out/soong/.intermediates/cts/tests/signature/api/cts-android-test-mock-current-api-gz/gen/android-test-mock-current.api.gz",
111        "out/soong/.intermediates/cts/tests/signature/api/cts-android-test-runner-current-api-gz/gen/android-test-runner-current.api.gz",
112        "out/soong/.intermediates/cts/tests/signature/api/cts-apache-http-legacy-current-api-gz/gen/apache-http-legacy-current.api.gz",
113        "out/soong/.intermediates/cts/tests/signature/api/cts-car-system-current-api-gz/gen/car-system-current.api.gz",
114        "out/soong/.intermediates/cts/tests/signature/api/cts-current-api-gz/android_common/gen/current.api.gz",
115        "out/soong/.intermediates/cts/tests/signature/api/cts-system-current-api-gz/android_common/gen/system-current.api.gz",
116        "out/soong/.intermediates/cts/tests/signature/api/cts-system-removed-api-gz/android_common/gen/system-removed.api.gz",
117
118    ]
119
120
121def default_dex_writer_files():
122    """:return: A representative sample list of dex writer related files created by the Android build using Metalava."""
123    return [
124        # This is not actually a dex writer file but it contains information derived from lots of dex writer files so
125        # any differences in the dex writer files will affect this file.
126        "out/soong/hiddenapi/hiddenapi-flags.csv",
127    ]
128
129
130def default_custom_files(top):
131    """Returns a representative sample list of custom files created by the Android build using a custom tool based, at
132
133    least in part, on Metalava.
134    :return: A list of custom files.
135    """
136    product_out = Path(os.environ.get("ANDROID_PRODUCT_OUT")).relative_to(top)
137    return [
138        f"{product_out}/obj/ETC/flag-api-mapping-{surface}_intermediates/flag-api-mapping-{surface}"
139        for surface in [
140            "PublicApi",
141            "SystemApi",
142            "ModuleLibApi",
143            "SystemServerApi",
144        ]
145    ]
146
147
148def construct_target_list(args, top):
149    """Generate a list of targets from the supplied arguments
150
151    :param args: the command line arguments.
152    :return: a non-empty list of targets to build.
153    """
154    targets = []
155    # If any custom options have been provided then build them.
156    if args.stub_src_jar:
157        targets += args.stub_src_jar
158    # If no custom targets have been provided then use the default targets.
159    if not targets:
160        targets += default_stub_files()
161        targets += default_doc_stub_files()
162        targets += default_jdiff_files()
163        targets += default_api_version_files()
164        targets += default_dex_writer_files()
165        targets += default_custom_files(top)
166    return targets
167
168
169def main(args):
170    top = os.environ.get("ANDROID_BUILD_TOP")
171    if not top:
172        raise Exception("ANDROID_BUILD_TOP not specified")
173    os.chdir(top)
174
175    # Parse command line arguments.
176    args = parse_command_line_args(args)
177
178    # Make sure that the output directory does not already exist.
179    output_dir = Path(args.directory)
180    if output_dir.exists():
181        raise Exception(f"{output_dir} exists, please delete or change")
182
183    # Construct the list of targets to build, using defaults where required.
184    targets = construct_target_list(args, top)
185
186    # Build the targets.
187    build_targets(targets)
188
189    # Create the output directory and copy the targets into it.
190    copy_targets(output_dir, targets)
191
192
193def copy_targets(output_dir, targets):
194    print(f"Making output directory: '{output_dir}'")
195    os.mkdir(output_dir)
196    print()
197    print(f"Copying the following targets into '{output_dir}':")
198    for t in targets:
199        print(f"    {t}")
200        shutil.copy(t, output_dir)
201    print()
202
203
204def build_targets(targets):
205    print()
206    print("Building the following targets:")
207    for t in targets:
208        print(f"    {t}")
209    print()
210    subprocess.run(
211        ["build/soong/soong_ui.bash", "--make-mode"] + targets, check=True
212    )
213    print()
214
215
216if __name__ == "__main__":
217    main(sys.argv[1:])
218