1#!/usr/bin/env python3
2#
3# Copyright (C) 2017 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import argparse
19from collections import defaultdict
20import glob
21import json
22import logging
23import os
24import sys
25
26import collect_licenses
27import utils
28
29
30class GenBuildFile(object):
31    """Generates Android.bp for VNDK snapshot.
32
33    VNDK snapshot directory structure under prebuilts/vndk/v{version}:
34        Android.bp
35        {SNAPSHOT_ARCH}/
36            Android.bp
37            arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
38                shared/
39                    vndk-core/
40                        (VNDK-core libraries, e.g. libbinder.so)
41                    vndk-sp/
42                        (VNDK-SP libraries, e.g. libc++.so)
43            arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/
44                shared/
45                    vndk-core/
46                        (VNDK-core libraries, e.g. libbinder.so)
47                    vndk-sp/
48                        (VNDK-SP libraries, e.g. libc++.so)
49            binder32/
50                (This directory is newly introduced in v28 (Android P) to hold
51                prebuilts built for 32-bit binder interface.)
52                Android.bp
53                arch-{TARGET_ARCH}-{TARGE_ARCH_VARIANT}/
54                    ...
55            configs/
56                (various *.txt configuration files, e.g. ld.config.*.txt)
57        ... (other {SNAPSHOT_ARCH}/ directories)
58        common/
59            Android.bp
60            NOTICE_FILES/
61                (license files, e.g. libfoo.so.txt)
62    """
63    INDENT = '    '
64    ETC_MODULES = [
65        'llndk.libraries.txt',
66        'vndksp.libraries.txt',
67        'vndkcore.libraries.txt',
68        'vndkprivate.libraries.txt',
69        'vndkproduct.libraries.txt',
70    ]
71
72    def __init__(self, install_dir, vndk_version):
73        """GenBuildFile constructor.
74
75        Args:
76          install_dir: string, absolute path to the prebuilts/vndk/v{version}
77            directory where the build files will be generated.
78          vndk_version: int, VNDK snapshot version (e.g. 30)
79        """
80        self._install_dir = install_dir
81        self._vndk_version = vndk_version
82        self._etc_paths = self._get_etc_paths()
83        self._snapshot_archs = utils.get_snapshot_archs(install_dir)
84        self._root_bpfile = os.path.join(install_dir, utils.ROOT_BP_PATH)
85        self._common_bpfile = os.path.join(install_dir, utils.COMMON_BP_PATH)
86        self._llndk = self._parse_lib_list(
87            os.path.basename(self._etc_paths['llndk.libraries.txt']))
88        self._vndk_core = self._parse_lib_list(
89            os.path.basename(self._etc_paths['vndkcore.libraries.txt']))
90        self._vndk_sp = self._parse_lib_list(
91            os.path.basename(self._etc_paths['vndksp.libraries.txt']))
92        self._vndk_private = self._parse_lib_list(
93            os.path.basename(self._etc_paths['vndkprivate.libraries.txt']))
94        self._vndk_product = self._parse_lib_list(
95            os.path.basename(self._etc_paths['vndkproduct.libraries.txt']))
96        self._modules_with_notice = self._get_modules_with_notice()
97        self._license_in_json = not self._modules_with_notice
98        self._license_kinds_map = defaultdict(set)
99        self._license_texts_map = defaultdict(set)
100        self.modules_with_restricted_lic = set()
101
102    def _get_etc_paths(self):
103        """Returns a map of relative file paths for each ETC module."""
104
105        etc_paths = dict()
106        for etc_module in self.ETC_MODULES:
107            etc_pattern = '{}*'.format(os.path.splitext(etc_module)[0])
108            globbed = glob.glob(
109                os.path.join(self._install_dir, utils.CONFIG_DIR_PATH_PATTERN,
110                             etc_pattern))
111            if len(globbed) > 0:
112                rel_etc_path = globbed[0].replace(self._install_dir, '')[1:]
113                etc_paths[etc_module] = rel_etc_path
114        return etc_paths
115
116    def _parse_lib_list(self, txt_filename):
117        """Returns a map of VNDK library lists per VNDK snapshot arch.
118
119        Args:
120          txt_filename: string, name of snapshot config file
121
122        Returns:
123          dict, e.g. {'arm64': ['libfoo.so', 'libbar.so', ...], ...}
124        """
125        lib_map = dict()
126        for txt_path in utils.find(self._install_dir, [txt_filename]):
127            arch = utils.snapshot_arch_from_path(txt_path)
128            abs_path_of_txt = os.path.join(self._install_dir, txt_path)
129            with open(abs_path_of_txt, 'r') as f:
130                lib_map[arch] = f.read().strip().split('\n')
131            if lib_map[arch] == ['']:
132                lib_map[arch].clear()
133        return lib_map
134
135    def _get_modules_with_notice(self):
136        """Returns a list of modules that have associated notice files. """
137        notice_paths = glob.glob(
138            os.path.join(self._install_dir, utils.NOTICE_FILES_DIR_PATH,
139                         '*.txt'))
140        return sorted(os.path.splitext(os.path.basename(p))[0] for p in notice_paths)
141
142    def generate_root_android_bp(self):
143        """Autogenerates Android.bp."""
144
145        logging.info('Generating Android.bp for snapshot v{}'.format(
146            self._vndk_version))
147        prebuilt_buildrules = []
148        for prebuilt in self.ETC_MODULES:
149            prebuilt_buildrules.append(self._gen_etc_prebuilt(prebuilt))
150
151        with open(self._root_bpfile, 'w') as bpfile:
152            bpfile.write(self._gen_autogen_msg('/'))
153            bpfile.write('\n')
154            bpfile.write(self._gen_license_package())
155            bpfile.write('\n')
156            bpfile.write(self._gen_license())
157            bpfile.write('\n')
158            bpfile.write('\n'.join(prebuilt_buildrules))
159            bpfile.write('\n')
160
161        logging.info('Successfully generated {}'.format(self._root_bpfile))
162
163    def generate_common_android_bp(self):
164        """Autogenerates common/Android.bp."""
165
166        logging.info('Generating common/Android.bp for snapshot v{}'.format(
167            self._vndk_version))
168        with open(self._common_bpfile, 'w') as bpfile:
169            bpfile.write(self._gen_autogen_msg('/'))
170            bpfile.write('\n')
171            bpfile.write(self._gen_license_package())
172            if self._license_in_json:
173                for name in self._license_kinds_map:
174                    bpfile.write('\n')
175                    bpfile.write(self._gen_notice_license(name))
176            else:
177                for module in self._modules_with_notice:
178                    bpfile.write('\n')
179                    bpfile.write(self._gen_notice_license(module))
180
181    def generate_android_bp(self):
182        """Autogenerates Android.bp."""
183
184        def gen_for_variant(arch, is_binder32=False):
185            """Generates Android.bp file for specified VNDK snapshot variant.
186
187            A VNDK snapshot variant is defined by the TARGET_ARCH and binder
188            bitness. Example snapshot variants:
189                vndk_v{ver}_arm:            {arch: arm, binder: 64-bit}
190                vndk_v{ver}_arm_binder32:   {arch: arm, binder: 32-bit}
191
192            Args:
193              arch: string, VNDK snapshot arch (e.g. 'arm64')
194              is_binder32: bool, True if binder interface is 32-bit
195            """
196            binder32_suffix = '_{}'.format(
197                utils.BINDER32) if is_binder32 else ''
198            logging.info('Generating Android.bp for vndk_v{}_{}{}'.format(
199                self._vndk_version, arch, binder32_suffix))
200
201            src_root = os.path.join(self._install_dir, arch)
202            module_names_txt = os.path.join(
203                src_root, "configs", "module_names.txt")
204            module_names = dict()
205            try:
206                with open(module_names_txt, 'r') as f:
207                    # Remove empty lines from module_names_txt
208                    module_list = filter(None, f.read().split('\n'))
209                for module in module_list:
210                    lib, name = module.split(' ')
211                    module_names[lib] = name
212            except IOError:
213                # If module_names.txt doesn't exist, ignore it and parse
214                # module names out from .so filenames. (old snapshot)
215                pass
216
217            variant_subpath = arch
218            if is_binder32:
219                variant_subpath = os.path.join(arch, utils.BINDER32)
220            variant_path = os.path.join(self._install_dir, variant_subpath)
221            bpfile_path = os.path.join(variant_path, 'Android.bp')
222
223            vndk_core_buildrules = self._gen_vndk_shared_prebuilts(
224                self._vndk_core[arch],
225                arch,
226                is_llndk=False,
227                is_vndk_sp=False,
228                is_binder32=is_binder32,
229                module_names=module_names)
230            vndk_sp_buildrules = self._gen_vndk_shared_prebuilts(
231                self._vndk_sp[arch],
232                arch,
233                is_llndk=False,
234                is_vndk_sp=True,
235                is_binder32=is_binder32,
236                module_names=module_names)
237            include_llndk = self._vndk_version > 30
238            if include_llndk:
239                llndk_buildrules = self._gen_vndk_shared_prebuilts(
240                    self._llndk[arch],
241                    arch,
242                    is_llndk=True,
243                    is_vndk_sp=False,
244                    is_binder32=is_binder32,
245                    module_names=module_names)
246
247            with open(bpfile_path, 'w') as bpfile:
248                bpfile.write(self._gen_autogen_msg('/'))
249                bpfile.write('\n')
250                bpfile.write(self._gen_license_package())
251                bpfile.write('\n')
252                bpfile.write('\n'.join(vndk_core_buildrules))
253                bpfile.write('\n')
254                bpfile.write('\n'.join(vndk_sp_buildrules))
255                if include_llndk:
256                    bpfile.write('\n')
257                    bpfile.write('\n'.join(llndk_buildrules))
258
259            variant_include_path = os.path.join(variant_path, 'include')
260            include_path = os.path.join(self._install_dir, arch, 'include')
261            if os.path.isdir(include_path) and variant_include_path != include_path:
262                os.symlink(os.path.relpath(include_path, variant_path),
263                    variant_include_path)
264
265            logging.info('Successfully generated {}'.format(bpfile_path))
266
267        for arch in self._snapshot_archs:
268            if os.path.isdir(
269                    os.path.join(self._install_dir, arch, utils.BINDER32)):
270                gen_for_variant(arch, is_binder32=True)
271            gen_for_variant(arch)
272
273    def _gen_autogen_msg(self, comment_char):
274        return ('{0}{0} THIS FILE IS AUTOGENERATED BY '
275                'development/vndk/snapshot/gen_buildfiles.py\n'
276                '{0}{0} DO NOT EDIT\n'.format(comment_char))
277
278    def _gen_license_package(self):
279        """ Generates license package for VNDK snapshot libs """
280        return ('package {{\n'
281                '{ind}default_applicable_licenses: ["prebuilts_vndk_v{version}_license"],\n'
282                '}}\n'.format(
283                    ind=self.INDENT,
284                    version=self._vndk_version))
285
286    def _get_license_kinds(self, module=''):
287        """ Returns a set of license kinds
288
289        Args:
290          module: module name to find the license kind.
291                  If empty, check all license files.
292        """
293        if self._license_in_json:
294            license_kinds = set()
295            if module == '':
296                # collect all license kinds
297                for kinds in self._license_kinds_map.values():
298                    license_kinds.update(kinds)
299                return license_kinds
300            else:
301                return self._license_kinds_map[module]
302
303        license_collector = collect_licenses.LicenseCollector(self._install_dir)
304        license_collector.run(module)
305        return license_collector.license_kinds
306
307    def _get_license_texts(self, module):
308        if self._license_in_json:
309            return {'{notice_dir}/{license_text}'.format(
310                        notice_dir=utils.NOTICE_FILES_DIR_NAME,
311                        license_text=license_text)
312                        for license_text in self._license_texts_map[module]}
313        else:
314            return {'{notice_dir}/{module}.txt'.format(
315                        notice_dir=utils.NOTICE_FILES_DIR_NAME,
316                        module=module)}
317
318    def _gen_license(self):
319        """ Generates license module.
320
321        It uses license files for all VNDK snapshot libraries in common/NOTICE_FILES directory.
322        """
323        license_kinds = self._get_license_kinds()
324        license_kinds_string = ''
325        for license_kind in sorted(license_kinds):
326            license_kinds_string += '{ind}{ind}"{license_kind}",\n'.format(
327                                    ind=self.INDENT, license_kind=license_kind)
328        return ('license {{\n'
329                '{ind}name: "prebuilts_vndk_v{version}_license",\n'
330                '{ind}visibility: [":__subpackages__"],\n'
331                '{ind}license_kinds: [\n'
332                '{license_kinds}'
333                '{ind}],\n'
334                '{ind}license_text: ["{notice_files}"],\n'
335                '}}\n'.format(
336                    ind=self.INDENT,
337                    version=self._vndk_version,
338                    license_kinds=license_kinds_string,
339                    notice_files=os.path.join(utils.NOTICE_FILES_DIR_PATH, '**', '*')))
340
341    def _get_versioned_name(self,
342                            prebuilt,
343                            arch,
344                            is_etc=False,
345                            is_binder32=False,
346                            module_names=None):
347        """Returns the VNDK version-specific module name for a given prebuilt.
348
349        The VNDK version-specific module name is defined as follows:
350        For a VNDK shared lib: 'libfoo.so'
351            if binder is 32-bit:
352                'libfoo.vndk.{version}.{arch}.binder32.vendor'
353            else:
354                'libfoo.vndk.{version}.{arch}.vendor'
355        For an ETC module: 'foo.txt' -> 'foo.{version}.txt'
356
357        Args:
358          prebuilt: string, name of the prebuilt object
359          arch: string, VNDK snapshot arch (e.g. 'arm64')
360          is_etc: bool, True if the LOCAL_MODULE_CLASS of prebuilt is 'ETC'
361          is_binder32: bool, True if binder interface is 32-bit
362          module_names: dict, module names for given prebuilts
363        """
364        if is_etc:
365            name, ext = os.path.splitext(prebuilt)
366            versioned_name = '{}.{}{}'.format(name, self._vndk_version, ext)
367        else:
368            module_names = module_names or dict()
369            if prebuilt in module_names:
370                name = module_names[prebuilt]
371            else:
372                name = os.path.splitext(prebuilt)[0]
373            binder_suffix = '.{}'.format(utils.BINDER32) if is_binder32 else ''
374            versioned_name = '{}.vndk.{}.{}{}.vendor'.format(
375                name, self._vndk_version, arch, binder_suffix)
376
377        return versioned_name
378
379    def _gen_etc_prebuilt(self, prebuilt):
380        """Generates build rule for an ETC prebuilt.
381
382        Args:
383          prebuilt: string, name of ETC prebuilt object
384        """
385        etc_path = self._etc_paths[prebuilt]
386        etc_sub_path = etc_path[etc_path.index('/') + 1:]
387
388        prebuilt_etc = ('prebuilt_etc {{\n'
389                        '{ind}name: "{versioned_name}",\n'
390                        '{ind}target: {{\n'.format(
391                            ind=self.INDENT,
392                            versioned_name=self._get_versioned_name(
393                                prebuilt, None, is_etc=True)))
394        for arch in self._snapshot_archs:
395            prebuilt_etc += ('{ind}{ind}android_{arch}: {{\n'
396                             '{ind}{ind}{ind}src: "{arch}/{etc_sub_path}",\n'
397                             '{ind}{ind}}},\n'.format(
398                                 ind=self.INDENT,
399                                 arch=arch,
400                                 etc_sub_path=etc_sub_path))
401        prebuilt_etc += ('{ind}}},\n'
402                         '}}\n'.format(ind=self.INDENT))
403        return prebuilt_etc
404
405    def _gen_prebuilt_library_shared(self, prebuilt_lib_info):
406        """Generates cc_prebuilt_library_shared modules for the old vendor
407        compatibility.
408
409        Some vendor modules still require old version of libraries that is not
410        available from the current source tree. To provide the old copy of the
411        libraries, use the vndk snapshot.
412
413        Args:
414            prebuilt_lib_info: pair of (string, list of strings), name of the
415                        prebuilt library and the list of shared libs for it.
416        """
417        lib_name = prebuilt_lib_info[0]
418        shared_libs = prebuilt_lib_info[1]
419
420        shared_libs_prop = ''
421        if shared_libs:
422            shared_libs_prop = ('{ind}shared_libs: [\n'.format(ind=self.INDENT))
423            for lib in shared_libs:
424                shared_libs_prop += ('{ind}{ind}"{lib}",\n'.format(
425                                        ind=self.INDENT, lib=lib))
426            shared_libs_prop += ('{ind}],\n'.format(ind=self.INDENT))
427
428        cc_prebuilt_libraries = ('cc_prebuilt_library_shared {{\n'
429                                 '{ind}name: "{name}-vendorcompat",\n'
430                                 '{ind}stem: "{name}",\n'
431                                 '{ind}vendor: true,\n'
432                                 '{ind}// These are already stripped, and '
433                                 'restripping them just issues diagnostics.\n'
434                                 '{ind}strip: {{\n'
435                                 '{ind}{ind}none: true,\n'
436                                 '{ind}}},\n'
437                                 '{shared_libs}'
438                                 '{ind}target: {{\n'.format(
439                                    ind=self.INDENT,
440                                    name=lib_name,
441                                    shared_libs=shared_libs_prop))
442        src_paths = utils.find(self._install_dir, [lib_name+'.so'])
443        for src in src_paths:
444            dirs = src.split(os.path.sep)
445            if len(dirs) < 3 or not dirs[1].startswith('arch-{}-'.format(dirs[0])):
446                continue
447            cc_prebuilt_libraries += ('{ind}{ind}android_{arch}: {{\n'
448                                      '{ind}{ind}{ind}srcs: ["{src}"],\n'
449                                      '{ind}{ind}}},\n'.format(
450                                        ind=self.INDENT, arch=dirs[0], src=src))
451        cc_prebuilt_libraries += ('{ind}}},\n'
452                                  '}}\n'.format(ind=self.INDENT))
453        return cc_prebuilt_libraries
454
455    def _gen_notice_license(self, module):
456        """Generates a notice license build rule for a given module.
457        When genererating each notice license, collect
458        modules_with_restricted_lic, the list of modules that are under the GPL.
459
460        Args:
461          module: string, module name
462        """
463        def has_restricted_license(license_kinds):
464            for lic in license_kinds:
465                if 'GPL' in lic:
466                    return True
467            return False
468
469        license_kinds = self._get_license_kinds(module)
470        if has_restricted_license(license_kinds):
471            self.modules_with_restricted_lic.add(module)
472        license_kinds_string = ''
473        for license_kind in sorted(license_kinds):
474            license_kinds_string += '{ind}{ind}"{license_kind}",\n'.format(
475                                    ind=self.INDENT, license_kind=license_kind)
476        license_texts = self._get_license_texts(module)
477        license_texts_string = ''
478        for license_text in sorted(license_texts):
479            license_texts_string += '{ind}{ind}"{license_text}",\n'.format(
480                                    ind=self.INDENT, license_text=license_text)
481        return ('license {{\n'
482                '{ind}name: "{license_name}",\n'
483                '{ind}license_kinds: [\n'
484                '{license_kinds}'
485                '{ind}],\n'
486                '{ind}license_text: [\n'
487                '{license_texts}'
488                '{ind}],\n'
489                '}}\n'.format(
490                    ind=self.INDENT,
491                    license_name=self._get_notice_license_name(module),
492                    license_kinds=license_kinds_string,
493                    license_texts=license_texts_string))
494
495    def _get_notice_license_name(self, module):
496        """ Gets a notice license module name for a given module.
497
498        Args:
499          notice: string, module name.
500        """
501        return 'vndk-v{ver}-{module}-license'.format(
502            ver=self._vndk_version, module=module)
503
504    def _gen_vndk_shared_prebuilts(self,
505                                   prebuilts,
506                                   arch,
507                                   is_llndk,
508                                   is_vndk_sp,
509                                   is_binder32,
510                                   module_names):
511        """Returns list of build rules for given prebuilts.
512
513        Args:
514          prebuilts: list of VNDK shared prebuilts
515          arch: string, VNDK snapshot arch (e.g. 'arm64')
516          is_llndk: bool, True if the prebuilts are LLNDK stubs
517          is_vndk_sp: bool, True if prebuilts are VNDK_SP libs
518          is_binder32: bool, True if binder interface is 32-bit
519          module_names: dict, module names for given prebuilts
520        """
521
522        module_prebuilts = dict()
523        for prebuilt in prebuilts:
524            if prebuilt in module_names:
525                name = module_names[prebuilt]
526            else:
527                name = os.path.splitext(prebuilt)[0]
528
529            if name not in module_prebuilts:
530                module_prebuilts[name] = list()
531            module_prebuilts[name].append(prebuilt)
532
533        build_rules = []
534        for name in module_prebuilts:
535            bp_module = self._gen_vndk_shared_prebuilt(
536                name,
537                arch,
538                srcs=module_prebuilts[name],
539                is_llndk=is_llndk,
540                is_vndk_sp=is_vndk_sp,
541                is_binder32=is_binder32)
542            if bp_module:
543                build_rules.append(bp_module)
544        return build_rules
545
546    def _gen_vndk_shared_prebuilt(self,
547                                  name,
548                                  arch,
549                                  srcs,
550                                  is_llndk,
551                                  is_vndk_sp,
552                                  is_binder32):
553        """Returns build rule for given prebuilt module, or an empty
554        string if the module is invalid (e.g. srcs doesn't exist).
555
556        Args:
557          name: string, name of prebuilt module
558          arch: string, VNDK snapshot arch (e.g. 'arm64')
559          srcs: list, prebuilt source file names of this module
560          is_llndk: bool, True if prebuilt is a LLNDK stub
561          is_vndk_sp: bool, True if prebuilt is a VNDK_SP lib
562          is_binder32: bool, True if binder interface is 32-bit
563        """
564
565        def is_prebuilts_in_list(prebuilts, vndk_list):
566            for prebuilt in prebuilts:
567                if prebuilt in vndk_list:
568                    return True
569            return False
570
571        def get_license_prop(name):
572            """Returns the license prop build rule.
573
574            Args:
575              name: string, name of the module
576            """
577            if name in self._license_kinds_map:
578                return '{ind}licenses: ["{license}"],\n'.format(
579                        ind=self.INDENT,
580                        license=self._get_notice_license_name(name))
581            return ''
582
583        def get_notice_file(prebuilts):
584            """Returns build rule for notice file (attribute 'licenses').
585
586            Args:
587              prebuilts: list, names of prebuilt objects
588            """
589            notice = ''
590            for prebuilt in prebuilts:
591                if prebuilt in self._modules_with_notice:
592                    notice = '{ind}licenses: ["{notice_license}"],\n'.format(
593                        ind=self.INDENT,
594                        notice_license=self._get_notice_license_name(prebuilt))
595                    break
596            return notice
597
598        def get_arch_props(name, arch, srcs_props):
599            """Returns build rule for arch specific srcs.
600
601            e.g.,
602                arch: {
603                    arm: {
604                        export_include_dirs: ["..."],
605                        export_system_include_dirs: ["..."],
606                        export_flags: ["..."],
607                        relative_install_path: "...",
608                        srcs: ["..."]
609                    },
610                    arm64: {
611                        export_include_dirs: ["..."],
612                        export_system_include_dirs: ["..."],
613                        export_flags: ["..."],
614                        relative_install_path: "...",
615                        srcs: ["..."]
616                    },
617                }
618
619            Args:
620              name: string, name of prebuilt module
621              arch: string, VNDK snapshot arch (e.g. 'arm64')
622              srcs_props: dict, prebuilt source paths and corresponding flags
623            """
624            arch_props = '{ind}arch: {{\n'.format(ind=self.INDENT)
625
626            def list_to_prop_value(l, name):
627                if len(l) == 0:
628                    return ''
629                dirs=',\n{ind}{ind}{ind}{ind}'.format(
630                    ind=self.INDENT).join(['"%s"' % d for d in l])
631                return ('{ind}{ind}{ind}{name}: [\n'
632                        '{ind}{ind}{ind}{ind}{dirs},\n'
633                        '{ind}{ind}{ind}],\n'.format(
634                            ind=self.INDENT,
635                            dirs=dirs,
636                            name=name))
637
638            def rename_generated_dirs(dirs):
639                # Rename out/soong/.intermediates to generated-headers for better readability.
640                return [d.replace(utils.SOONG_INTERMEDIATES_DIR, utils.GENERATED_HEADERS_DIR, 1) for d in dirs]
641
642            for src in sorted(srcs_props.keys()):
643                include_dirs = ''
644                system_include_dirs = ''
645                flags = ''
646                relative_install_path = ''
647                props = srcs_props[src]
648                if 'ExportedDirs' in props:
649                    dirs = rename_generated_dirs(props['ExportedDirs'])
650                    l = ['include/%s' % d for d in dirs]
651                    include_dirs = list_to_prop_value(l, 'export_include_dirs')
652                if 'ExportedSystemDirs' in props:
653                    dirs = rename_generated_dirs(props['ExportedSystemDirs'])
654                    l = ['include/%s' % d for d in dirs]
655                    system_include_dirs = list_to_prop_value(l, 'export_system_include_dirs')
656                if 'ExportedFlags' in props:
657                    flags = list_to_prop_value(props['ExportedFlags'], 'export_flags')
658                if 'RelativeInstallPath' in props:
659                    relative_install_path = ('{ind}{ind}{ind}'
660                        'relative_install_path: "{path}",\n').format(
661                            ind=self.INDENT,
662                            path=props['RelativeInstallPath'])
663                if 'LicenseKinds' in props:
664                    self._license_kinds_map[name].update(props['LicenseKinds'])
665                if 'LicenseTexts' in props:
666                    self._license_texts_map[name].update(props['LicenseTexts'])
667
668                arch_props += ('{ind}{ind}{arch}: {{\n'
669                               '{include_dirs}'
670                               '{system_include_dirs}'
671                               '{flags}'
672                               '{relative_install_path}'
673                               '{ind}{ind}{ind}srcs: ["{src}"],\n'
674                               '{ind}{ind}}},\n').format(
675                                  ind=self.INDENT,
676                                  arch=utils.prebuilt_arch_from_path(
677                                      os.path.join(arch, src)),
678                                  include_dirs=include_dirs,
679                                  system_include_dirs=system_include_dirs,
680                                  flags=flags,
681                                  relative_install_path=relative_install_path,
682                                  src=src)
683            arch_props += '{ind}}},\n'.format(ind=self.INDENT)
684            return arch_props
685
686        src_root = os.path.join(self._install_dir, arch)
687        if is_binder32:
688            src_root = os.path.join(src_root, utils.BINDER32)
689
690        src_paths = utils.find(src_root, srcs)
691        # filter out paths under 'binder32' subdirectory
692        src_paths = list(filter(lambda src: not src.startswith(utils.BINDER32),
693            src_paths))
694        # This module is invalid if no srcs are found.
695        if not src_paths:
696            logging.info('No srcs found for {}; skipping'.format(name))
697            return ""
698
699        product_available = ''
700        # if vndkproduct.libraries.txt is empty, make the VNDKs available to product by default.
701        if is_llndk or not self._vndk_product[arch] or is_prebuilts_in_list(srcs, self._vndk_product[arch]):
702            product_available = '{ind}product_available: true,\n'.format(
703                ind=self.INDENT)
704
705        vndk_props = ''
706        if not is_llndk:
707            vndk_sp = ''
708            if is_vndk_sp:
709                vndk_sp = '{ind}{ind}support_system_process: true,\n'.format(
710                    ind=self.INDENT)
711
712            vndk_private = ''
713            if is_prebuilts_in_list(srcs, self._vndk_private[arch]):
714                vndk_private = '{ind}{ind}private: true,\n'.format(
715                    ind=self.INDENT)
716
717            vndk_props = ('{ind}vndk: {{\n'
718                          '{ind}{ind}enabled: true,\n'
719                          '{vndk_sp}'
720                          '{vndk_private}'
721                          '{ind}}},\n'.format(
722                              ind=self.INDENT,
723                              product_available=product_available,
724                              vndk_sp=vndk_sp,
725                              vndk_private=vndk_private))
726
727        srcs_props = dict()
728        for src in src_paths:
729            props = dict()
730            prop_path = os.path.join(src_root, src+'.json')
731            try:
732                with open(prop_path, 'r') as f:
733                    props = json.loads(f.read())
734                os.unlink(prop_path)
735            except:
736                # TODO(b/70312118): Parse from soong build system
737                if name == 'android.hidl.memory@1.0-impl':
738                    props['RelativeInstallPath'] = 'hw'
739            srcs_props[src] = props
740        arch_props = get_arch_props(name, arch, srcs_props)
741
742        if self._license_in_json:
743            license = get_license_prop(name)
744        else:
745            license = get_notice_file(srcs)
746
747        binder32bit = ''
748        if is_binder32:
749            binder32bit = '{ind}binder32bit: true,\n'.format(ind=self.INDENT)
750
751        min_sdk_version = ''
752        for src, props in srcs_props.items():
753            if 'MinSdkVersion' in props:
754                min_sdk_version = '{ind}min_sdk_version: "{ver}",\n'.format(
755                    ind=self.INDENT,
756                    ver=props['MinSdkVersion'])
757                break
758
759        return ('vndk_prebuilt_shared {{\n'
760                '{ind}name: "{name}",\n'
761                '{ind}version: "{ver}",\n'
762                '{ind}target_arch: "{target_arch}",\n'
763                '{binder32bit}'
764                '{ind}vendor_available: true,\n'
765                '{product_available}'
766                '{vndk_props}'
767                '{min_sdk_version}'
768                '{license}'
769                '{arch_props}'
770                '}}\n'.format(
771                    ind=self.INDENT,
772                    name=name,
773                    ver=self._vndk_version,
774                    target_arch=arch,
775                    binder32bit=binder32bit,
776                    product_available=product_available,
777                    vndk_props=vndk_props,
778                    min_sdk_version=min_sdk_version,
779                    license=license,
780                    arch_props=arch_props))
781
782
783def get_args():
784    parser = argparse.ArgumentParser()
785    parser.add_argument(
786        'vndk_version',
787        type=utils.vndk_version_int,
788        help='VNDK snapshot version to install, e.g. "{}".'.format(
789            utils.MINIMUM_VNDK_VERSION))
790    parser.add_argument(
791        '-v',
792        '--verbose',
793        action='count',
794        default=0,
795        help='Increase output verbosity, e.g. "-v", "-vv".')
796    return parser.parse_args()
797
798
799def main():
800    """For local testing purposes.
801
802    Note: VNDK snapshot must be already installed under
803      prebuilts/vndk/v{version}.
804    """
805    ANDROID_BUILD_TOP = utils.get_android_build_top()
806    PREBUILTS_VNDK_DIR = utils.join_realpath(ANDROID_BUILD_TOP,
807                                             'prebuilts/vndk')
808
809    args = get_args()
810    vndk_version = args.vndk_version
811    install_dir = os.path.join(PREBUILTS_VNDK_DIR, 'v{}'.format(vndk_version))
812    if not os.path.isdir(install_dir):
813        raise ValueError(
814            'Please provide valid VNDK version. {} does not exist.'
815            .format(install_dir))
816    utils.set_logging_config(args.verbose)
817
818    buildfile_generator = GenBuildFile(install_dir, vndk_version)
819    # To parse json information, read and generate arch android.bp using
820    # generate_android_bp() first.
821    buildfile_generator.generate_android_bp()
822    buildfile_generator.generate_root_android_bp()
823    buildfile_generator.generate_common_android_bp()
824
825    logging.info('Done.')
826
827
828if __name__ == '__main__':
829    main()
830