1#!/usr/bin/env python3
2
3import argparse
4import collections
5import os
6import shutil
7import time
8
9from utils import (
10    AOSP_DIR, COMPRESSED_SOURCE_ABI_DUMP_EXT, SOURCE_ABI_DUMP_EXT,
11    SOURCE_ABI_DUMP_EXT_END, SO_EXT, Target, copy_reference_dump,
12    find_lib_lsdumps, get_build_vars_for_product, make_libraries, make_tree,
13    read_lsdump_paths)
14
15
16PRODUCTS_DEFAULT = ['aosp_arm', 'aosp_arm64', 'aosp_x86', 'aosp_x86_64']
17
18PREBUILTS_ABI_DUMPS_DEFAULT = os.path.join(AOSP_DIR, 'prebuilts', 'abi-dumps')
19
20SOONG_DIR = os.path.join(AOSP_DIR, 'out', 'soong', '.intermediates')
21
22
23def choose_vndk_version(version, platform_vndk_version, board_vndk_version):
24    if version is None:
25        # This logic must be in sync with the logic for reference ABI dumps
26        # directory in `build/soong/cc/library.go`.
27        version = platform_vndk_version
28        if board_vndk_version not in ('current', ''):
29            version = board_vndk_version
30    return version
31
32
33def make_libs_for_product(libs, product, variant, vndk_version, targets):
34    print('making libs for', product + '-' + variant)
35    if libs:
36        make_libraries(product, variant, vndk_version, targets, libs)
37    else:
38        make_tree(product, variant)
39
40
41def get_ref_dump_dir_stem(ref_dump_dir, category, chosen_vndk_version,
42                          binder_bitness, arch):
43    return os.path.join(ref_dump_dir, category, chosen_vndk_version,
44                        binder_bitness, arch)
45
46
47def find_and_remove_path(root_path, file_name=None):
48    if file_name is not None:
49        root_path = os.path.join(root_path, 'source-based', file_name)
50
51    if os.path.exists(root_path):
52        print('removing', root_path)
53        if os.path.isfile(root_path):
54            os.remove(root_path)
55        else:
56            shutil.rmtree(root_path)
57
58
59def remove_references_for_all_arches_and_variants(ref_dump_dir,
60                                                  chosen_vndk_version,
61                                                  binder_bitness, targets,
62                                                  libs):
63    for target in targets:
64        if target.arch == '' or target.arch_variant == '':
65            continue
66        for category in ('ndk', 'platform', 'vndk'):
67            dir_to_remove = get_ref_dump_dir_stem(
68                ref_dump_dir, category, chosen_vndk_version, binder_bitness,
69                target.get_arch_str())
70            if libs:
71                for lib in libs:
72                    find_and_remove_path(dir_to_remove,
73                                         lib + SOURCE_ABI_DUMP_EXT)
74                    find_and_remove_path(dir_to_remove,
75                                         lib + COMPRESSED_SOURCE_ABI_DUMP_EXT)
76            else:
77                find_and_remove_path(dir_to_remove)
78
79
80def tag_to_dir_name(tag):
81    if tag == 'NDK':
82        return 'ndk'
83    if tag == 'PLATFORM':
84        return 'platform'
85    if tag.startswith('VNDK') or tag == 'LLNDK':
86        return 'vndk'
87    raise ValueError(tag + 'is not a known tag.')
88
89
90def find_and_copy_lib_lsdumps(ref_dump_dir, chosen_vndk_version,
91                              binder_bitness, target, libs, lsdump_paths,
92                              compress):
93    arch_lsdump_paths = find_lib_lsdumps(lsdump_paths, libs, target)
94
95    num_created = 0
96    for tag, path in arch_lsdump_paths:
97        ref_dump_dir_stem = get_ref_dump_dir_stem(
98            ref_dump_dir, tag_to_dir_name(tag), chosen_vndk_version,
99            binder_bitness, target.get_arch_str())
100        copy_reference_dump(
101            path, os.path.join(ref_dump_dir_stem, 'source-based'), compress)
102        num_created += 1
103    return num_created
104
105
106def create_source_abi_reference_dumps(args, chosen_vndk_version,
107                                      binder_bitness, lsdump_paths, targets):
108    num_libs_copied = 0
109    for target in targets:
110        if target.arch == '' or target.arch_variant == '':
111            continue
112
113        print('Creating dumps for target_arch:', target.arch, 'and variant ',
114              target.arch_variant)
115        assert target.primary_arch != ''
116
117        num_libs_copied += find_and_copy_lib_lsdumps(
118            args.ref_dump_dir, chosen_vndk_version, binder_bitness, target,
119            args.libs, lsdump_paths, args.compress)
120    return num_libs_copied
121
122
123def create_source_abi_reference_dumps_for_all_products(args):
124    """Create reference ABI dumps for all specified products."""
125
126    num_processed = 0
127
128    for product in args.products:
129        build_vars = get_build_vars_for_product(
130            ['PLATFORM_VNDK_VERSION', 'BOARD_VNDK_VERSION', 'BINDER32BIT'],
131            product, args.build_variant)
132
133        platform_vndk_version = build_vars[0]
134        board_vndk_version = build_vars[1]
135        if build_vars[2] == 'true':
136            binder_bitness = '32'
137        else:
138            binder_bitness = '64'
139
140        chosen_vndk_version = choose_vndk_version(
141            args.version, platform_vndk_version, board_vndk_version)
142
143        targets = [Target(True, product), Target(False, product)]
144        # Remove reference ABI dumps specified in `args.libs` (or remove all of
145        # them if none of them are specified) so that we may build these
146        # libraries successfully.
147        remove_references_for_all_arches_and_variants(
148            args.ref_dump_dir, chosen_vndk_version, binder_bitness, targets,
149            args.libs)
150
151        if not args.no_make_lib:
152            # Build all the specified libs, or build `findlsdumps` if no libs
153            # are specified.
154            make_libs_for_product(args.libs, product, args.build_variant,
155                                  platform_vndk_version, targets)
156
157        lsdump_paths = read_lsdump_paths(product, args.build_variant,
158                                         platform_vndk_version, targets,
159                                         build=False)
160
161        num_processed += create_source_abi_reference_dumps(
162            args, chosen_vndk_version, binder_bitness, lsdump_paths, targets)
163
164    return num_processed
165
166
167def _parse_args():
168    """Parse the command line arguments."""
169
170    parser = argparse.ArgumentParser()
171    parser.add_argument('--version', help='VNDK version')
172    parser.add_argument('--no-make-lib', action='store_true',
173                        help='no m -j lib.vendor while creating reference')
174    parser.add_argument('--llndk', action='store_true',
175                        help='the flag is deprecated and has no effect')
176    parser.add_argument('-libs', action='append',
177                        help='libs to create references for')
178    parser.add_argument('-products', action='append',
179                        help='products to create references for')
180    parser.add_argument('--build-variant', default='userdebug',
181                        help='build variant to create references for')
182    parser.add_argument('--compress', action='store_true',
183                        help='compress reference dump with gzip')
184    parser.add_argument('-ref-dump-dir',
185                        help='directory to copy reference abi dumps into',
186                        default=PREBUILTS_ABI_DUMPS_DEFAULT)
187
188    args = parser.parse_args()
189
190    if args.libs:
191        if any(lib_name.endswith(SOURCE_ABI_DUMP_EXT_END) or
192               lib_name.endswith(SO_EXT) for lib_name in args.libs):
193            parser.error('-libs should be followed by a base name without '
194                         'file extension.')
195
196    if args.products is None:
197        # If `args.products` is unspecified, generate reference ABI dumps for
198        # all products.
199        args.products = PRODUCTS_DEFAULT
200
201    return args
202
203
204def main():
205    args = _parse_args()
206
207    start = time.time()
208    num_processed = create_source_abi_reference_dumps_for_all_products(args)
209    end = time.time()
210
211    print()
212    print('msg: Processed', num_processed, 'libraries in ', (end - start) / 60,
213          ' minutes')
214
215
216if __name__ == '__main__':
217    main()
218