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