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