blob: 188c13a6f4c05937ce1d60f0da1c944054c44afe [file] [log] [blame]
#!/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:])