xref: /aosp_15_r20/development/tools/ndk/ndkabidump/__init__.py (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
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 version_dir in abi_out.iterdir():
63            try:
64                int(version_dir.name)
65            except ValueError:
66                logger().info("Skipping %s because it is a preview API level", version_dir)
67                continue
68
69            for dump in version_dir.glob("**/abi.stg"):
70                install_path = prebuilts_dir / dump.relative_to(abi_out)
71                install_dir = install_path.parent
72                if not install_dir.exists():
73                    install_dir.mkdir(parents=True)
74                logger().info(f"Copying ABI dump {dump} to {install_path}")
75                shutil.copy2(dump, install_path)
76
77    def run(self) -> None:
78        """Runs the updater.
79
80        Cleans the out directory, builds the ABI dumps, and copies the results
81        to the prebuilts directory.
82        """
83        self.build_abi_dumps()
84        self.copy_updated_abi_dumps()
85
86
87HELP = """\
88Builds and updates the NDK ABI prebuilts.
89
90Whenever a change is made that alters the NDK ABI (or an API level is
91finalized, or a new preview codename is introduced to the build), the prebuilts
92in prebuilts/abi-dumps/ndk need to be updated to match. For any finalized APIs,
93the breaking change typically needs to be reverted.
94
95Note that typically this tool should be executed via
96development/tools/ndk/update_ndk_abi.sh. That script will ensure that this tool
97is up-to-date and run with the correct arguments.
98"""
99
100
101class App:
102    """Command line application from updating NDK ABI prebuilts."""
103
104    @staticmethod
105    def parse_args() -> argparse.Namespace:
106        """Parses and returns command line arguments."""
107
108        parser = argparse.ArgumentParser(
109            formatter_class=argparse.RawDescriptionHelpFormatter, description=HELP
110        )
111
112        def resolved_path(path: str) -> Path:
113            """Converts a string into a fully resolved Path."""
114            return Path(path).resolve()
115
116        parser.add_argument(
117            "--src-dir",
118            type=resolved_path,
119            required=True,
120            help="Path to the top of the Android source tree.",
121        )
122
123        parser.add_argument(
124            "out_dir",
125            type=resolved_path,
126            metavar="OUT_DIR",
127            help="Output directory to use for building ABI dumps.",
128        )
129
130        parser.add_argument(
131            "-v",
132            "--verbose",
133            action="count",
134            default=0,
135            help="Increase logging verbosity.",
136        )
137
138        return parser.parse_args()
139
140    def run(self) -> None:
141        """Builds the new NDK ABI dumps and copies them to prebuilts."""
142        args = self.parse_args()
143        log_level = logging.DEBUG if args.verbose else logging.INFO
144        logging.basicConfig(level=log_level)
145        test_path = args.src_dir / "build/soong/soong_ui.bash"
146        if not test_path.exists():
147            sys.exit(
148                f"Source directory {args.src_dir} does not appear to be an "
149                f"Android source tree: {test_path} does not exist."
150            )
151
152        Updater(args.src_dir, args.out_dir).run()
153