1# 2# Copyright (C) 2021 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# 16"""Tool for updating the prebuilt NDK ABI dumps.""" 17import argparse 18import logging 19from pathlib import Path 20import shutil 21import sys 22 23from .soong import Soong 24 25 26def logger() -> logging.Logger: 27 """Returns the module level logger.""" 28 return logging.getLogger(__name__) 29 30 31class Updater: 32 """Tool for updating prebuilt NDK ABI dumps.""" 33 34 def __init__(self, src_dir: Path, build_dir: Path) -> None: 35 self.src_dir = src_dir 36 self.build_dir = build_dir 37 38 def build_abi_dumps(self) -> None: 39 """Builds the updated NDK ABI dumps.""" 40 soong = Soong(self.src_dir, self.build_dir) 41 logger().info(f"Building ABI dumps to {self.build_dir}") 42 soong.build( 43 ["dump-ndk-abi"], 44 env={ 45 "TARGET_PRODUCT": "ndk", 46 "TARGET_RELEASE": "trunk_staging", 47 # TODO: remove ALLOW_MISSING_DEPENDENCIES=true when all the 48 # riscv64 dependencies exist (currently blocked by 49 # http://b/273792258). 50 "ALLOW_MISSING_DEPENDENCIES": "true", 51 # TODO: remove BUILD_BROKEN_DISABLE_BAZEL=1 when bazel supports 52 # riscv64 (http://b/262192655). 53 "BUILD_BROKEN_DISABLE_BAZEL": "1", 54 }, 55 ) 56 57 def copy_updated_abi_dumps(self) -> None: 58 """Copies the NDK ABI dumps from the build directory to prebuilts.""" 59 prebuilts_project = self.src_dir / "prebuilts/abi-dumps" 60 prebuilts_dir = prebuilts_project / "ndk" 61 abi_out = self.build_dir / "soong/abi-dumps/ndk" 62 for dump in abi_out.glob("**/abi.stg"): 63 install_path = prebuilts_dir / dump.relative_to(abi_out) 64 install_dir = install_path.parent 65 if not install_dir.exists(): 66 install_dir.mkdir(parents=True) 67 logger().info(f"Copying ABI dump {dump} to {install_path}") 68 shutil.copy2(dump, install_path) 69 70 def run(self) -> None: 71 """Runs the updater. 72 73 Cleans the out directory, builds the ABI dumps, and copies the results 74 to the prebuilts directory. 75 """ 76 self.build_abi_dumps() 77 self.copy_updated_abi_dumps() 78 79 80HELP = """\ 81Builds and updates the NDK ABI prebuilts. 82 83Whenever a change is made that alters the NDK ABI (or an API level is 84finalized, or a new preview codename is introduced to the build), the prebuilts 85in prebuilts/abi-dumps/ndk need to be updated to match. For any finalized APIs, 86the breaking change typically needs to be reverted. 87 88Note that typically this tool should be executed via 89development/tools/ndk/update_ndk_abi.sh. That script will ensure that this tool 90is up-to-date and run with the correct arguments. 91""" 92 93 94class App: 95 """Command line application from updating NDK ABI prebuilts.""" 96 97 @staticmethod 98 def parse_args() -> argparse.Namespace: 99 """Parses and returns command line arguments.""" 100 101 parser = argparse.ArgumentParser( 102 formatter_class=argparse.RawDescriptionHelpFormatter, description=HELP 103 ) 104 105 def resolved_path(path: str) -> Path: 106 """Converts a string into a fully resolved Path.""" 107 return Path(path).resolve() 108 109 parser.add_argument( 110 "--src-dir", 111 type=resolved_path, 112 required=True, 113 help="Path to the top of the Android source tree.", 114 ) 115 116 parser.add_argument( 117 "out_dir", 118 type=resolved_path, 119 metavar="OUT_DIR", 120 help="Output directory to use for building ABI dumps.", 121 ) 122 123 parser.add_argument( 124 "-v", 125 "--verbose", 126 action="count", 127 default=0, 128 help="Increase logging verbosity.", 129 ) 130 131 return parser.parse_args() 132 133 def run(self) -> None: 134 """Builds the new NDK ABI dumps and copies them to prebuilts.""" 135 args = self.parse_args() 136 log_level = logging.DEBUG if args.verbose else logging.INFO 137 logging.basicConfig(level=log_level) 138 test_path = args.src_dir / "build/soong/soong_ui.bash" 139 if not test_path.exists(): 140 sys.exit( 141 f"Source directory {args.src_dir} does not appear to be an " 142 f"Android source tree: {test_path} does not exist." 143 ) 144 145 Updater(args.src_dir, args.out_dir).run() 146