| #!/usr/bin/env -S python3 -u |
| # Copyright (C) 2024 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import argparse |
| import os |
| from pathlib import Path |
| import shutil |
| import subprocess |
| import sys |
| |
| # Formatted using: pyformat -s 4 --force_quote_type double -i scripts/gather-android-metalava-artifacts.py |
| |
| |
| def parse_command_line_args(args): |
| """Define the command line options and the parse the command line arguments with them. |
| |
| :param args: the command line arguments :return: Return the result of |
| parsing the command line arguments. |
| """ |
| args_parser = argparse.ArgumentParser( |
| description=( |
| "Gather Android artifacts created by Metalava. This will build and" |
| " then copy a set of targets to the output directory. If no custom" |
| " targets are provided then a set of default ones will be provided" |
| " that covers stub generation, signature to JDiff conversion and" |
| " api-versions.xml file generation. The intent is that this would" |
| " be run this before and after making the change to build and copy" |
| " the artifacts into two separate directories that can then be" |
| " compared to see what, if any, changes have happened. This does" |
| " not check signature file generation as that can be easily checked" |
| " by running `m checkapi`." |
| ), |
| ) |
| args_parser.add_argument( |
| "directory", |
| help="Output directory into which artifacts will be copied.", |
| ) |
| args_parser.add_argument( |
| "--stub-src-jar", |
| action="append", |
| help="Additional stub jar to gather", |
| ) |
| |
| return args_parser.parse_args(args) |
| |
| |
| def default_stub_files(): |
| """:return: A representative sample list of stub source jars generated by the Android build using Metalava""" |
| return [ |
| f"out/target/common/docs/{x}-stubs.srcjar" |
| for x in [ |
| "api-stubs-docs-non-updatable", |
| "system-api-stubs-docs-non-updatable", |
| "test-api-stubs-docs-non-updatable", |
| "module-lib-api-stubs-docs-non-updatable", |
| ] |
| ] |
| |
| |
| def default_doc_stub_files(): |
| """:return: A representative sample list of doc stub source jars generated by the Android build using Metalava""" |
| return [ |
| "out/target/common/docs/framework-doc-stubs-stubs.srcjar", |
| "out/target/common/docs/framework-doc-system-stubs-stubs.srcjar", |
| ] |
| |
| |
| def default_api_version_files(): |
| """:return: A representative sample list of `api-versions.xml` files generated by the Android build using |
| |
| Metalava. |
| """ |
| return [ |
| "out/soong/lint/api_versions_public.xml", |
| "out/soong/lint/api_versions_system.xml", |
| "out/soong/lint/api_versions_module_lib.xml", |
| "out/soong/lint/api_versions_system_server.xml", |
| ] |
| |
| |
| def default_jdiff_files(): |
| """:return: A representative sample list of JDiff files created by the Android build using Metalava.""" |
| return [ |
| # JDiff files generated from jar files. |
| "out/target/common/obj/api.xml", |
| "out/target/common/obj/system-api.xml", |
| "out/target/common/obj/module-lib-api.xml", |
| "out/target/common/obj/system-server-api.xml", |
| "out/target/common/obj/test-api.xml", |
| # JDiff files generated from txt files. |
| "out/soong/.intermediates/packages/services/Car/car-lib/android.car-test-stubs-jdiff/gen/car-test-api.xml", |
| "out/soong/.intermediates/packages/services/Car/car-lib/android.car-stubs-jdiff/gen/car-api.xml", |
| "out/soong/.intermediates/packages/services/Car/car-lib/android.car-system-stubs-jdiff/gen/car-system-api.xml", |
| ""# JDiff files generated from txt files and then compressed using gzip.. |
| "out/soong/.intermediates/cts/tests/signature/api/cts-android-test-base-current-api-gz/gen/android-test-base-current.api.gz", |
| "out/soong/.intermediates/cts/tests/signature/api/cts-android-test-mock-current-api-gz/gen/android-test-mock-current.api.gz", |
| "out/soong/.intermediates/cts/tests/signature/api/cts-android-test-runner-current-api-gz/gen/android-test-runner-current.api.gz", |
| "out/soong/.intermediates/cts/tests/signature/api/cts-apache-http-legacy-current-api-gz/gen/apache-http-legacy-current.api.gz", |
| "out/soong/.intermediates/cts/tests/signature/api/cts-car-system-current-api-gz/gen/car-system-current.api.gz", |
| "out/soong/.intermediates/cts/tests/signature/api/cts-current-api-gz/gen/current.api.gz", |
| "out/soong/.intermediates/cts/tests/signature/api/cts-system-current-api-gz/gen/system-current.api.gz", |
| "out/soong/.intermediates/cts/tests/signature/api/cts-system-removed-api-gz/gen/system-removed.api.gz", |
| |
| ] |
| |
| |
| def default_dex_writer_files(): |
| """:return: A representative sample list of dex writer related files created by the Android build using Metalava.""" |
| return [ |
| # This is not actually a dex writer file but it contains information derived from lots of dex writer files so |
| # any differences in the dex writer files will affect this file. |
| "out/soong/hiddenapi/hiddenapi-flags.csv", |
| ] |
| |
| |
| def default_custom_files(top): |
| """Returns a representative sample list of custom files created by the Android build using a custom tool based, at |
| |
| least in part, on Metalava. |
| :return: A list of custom files. |
| """ |
| product_out = Path(os.environ.get("ANDROID_PRODUCT_OUT")).relative_to(top) |
| return [ |
| f"{product_out}/obj/ETC/flag-api-mapping-{surface}_intermediates/flag-api-mapping-{surface}" |
| for surface in [ |
| "PublicApi", |
| "SystemApi", |
| "ModuleLibApi", |
| "SystemServerApi", |
| ] |
| ] |
| |
| |
| def construct_target_list(args, top): |
| """Generate a list of targets from the supplied arguments |
| |
| :param args: the command line arguments. |
| :return: a non-empty list of targets to build. |
| """ |
| targets = [] |
| # If any custom options have been provided then build them. |
| if args.stub_src_jar: |
| targets += args.stub_src_jar |
| # If no custom targets have been provided then use the default targets. |
| if not targets: |
| targets += default_stub_files() |
| targets += default_doc_stub_files() |
| targets += default_jdiff_files() |
| targets += default_api_version_files() |
| targets += default_dex_writer_files() |
| targets += default_custom_files(top) |
| return targets |
| |
| |
| def main(args): |
| top = os.environ.get("ANDROID_BUILD_TOP") |
| if not top: |
| raise Exception("ANDROID_BUILD_TOP not specified") |
| os.chdir(top) |
| |
| # Parse command line arguments. |
| args = parse_command_line_args(args) |
| |
| # Make sure that the output directory does not already exist. |
| output_dir = Path(args.directory) |
| if output_dir.exists(): |
| raise Exception(f"{output_dir} exists, please delete or change") |
| |
| # Construct the list of targets to build, using defaults where required. |
| targets = construct_target_list(args, top) |
| |
| # Build the targets. |
| build_targets(targets) |
| |
| # Create the output directory and copy the targets into it. |
| copy_targets(output_dir, targets) |
| |
| |
| def copy_targets(output_dir, targets): |
| print(f"Making output directory: '{output_dir}'") |
| os.mkdir(output_dir) |
| print() |
| print(f"Copying the following targets into '{output_dir}':") |
| for t in targets: |
| print(f" {t}") |
| shutil.copy(t, output_dir) |
| print() |
| |
| |
| def build_targets(targets): |
| print() |
| print("Building the following targets:") |
| for t in targets: |
| print(f" {t}") |
| print() |
| subprocess.run( |
| ["build/soong/soong_ui.bash", "--make-mode"] + targets, check=True |
| ) |
| print() |
| |
| |
| if __name__ == "__main__": |
| main(sys.argv[1:]) |