• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
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
19import glob
20import logging
21import os
22import sys
23
24import utils
25
26
27class GenBuildFile(object):
28    """Generates Android.bp for VNDK snapshot.
29
30    VNDK snapshot directory structure under prebuilts/vndk/v{version}:
31        Android.bp
32        {SNAPSHOT_ARCH}/
33            Android.bp
34            arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
35                shared/
36                    vndk-core/
37                        (VNDK-core libraries, e.g. libbinder.so)
38                    vndk-sp/
39                        (VNDK-SP libraries, e.g. libc++.so)
40            arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/
41                shared/
42                    vndk-core/
43                        (VNDK-core libraries, e.g. libbinder.so)
44                    vndk-sp/
45                        (VNDK-SP libraries, e.g. libc++.so)
46            binder32/
47                (This directory is newly introduced in v28 (Android P) to hold
48                prebuilts built for 32-bit binder interface.)
49                Android.bp
50                arch-{TARGET_ARCH}-{TARGE_ARCH_VARIANT}/
51                    ...
52            configs/
53                (various *.txt configuration files, e.g. ld.config.*.txt)
54        ... (other {SNAPSHOT_ARCH}/ directories)
55        common/
56            Android.bp
57            NOTICE_FILES/
58                (license files, e.g. libfoo.so.txt)
59    """
60    INDENT = '    '
61    ETC_MODULES = [
62        'ld.config.txt', 'llndk.libraries.txt', 'vndksp.libraries.txt'
63    ]
64
65    # TODO(b/70312118): Parse from soong build system
66    RELATIVE_INSTALL_PATHS = {'android.hidl.memory@1.0-impl.so': 'hw'}
67
68    def __init__(self, install_dir, vndk_version):
69        """GenBuildFile constructor.
70
71        Args:
72          install_dir: string, absolute path to the prebuilts/vndk/v{version}
73            directory where the build files will be generated.
74          vndk_version: int, VNDK snapshot version (e.g., 27, 28)
75        """
76        self._install_dir = install_dir
77        self._vndk_version = vndk_version
78        self._etc_paths = self._get_etc_paths()
79        self._snapshot_archs = utils.get_snapshot_archs(install_dir)
80        self._root_bpfile = os.path.join(install_dir, utils.ROOT_BP_PATH)
81        self._common_bpfile = os.path.join(install_dir, utils.COMMON_BP_PATH)
82        self._vndk_core = self._parse_lib_list('vndkcore.libraries.txt')
83        self._vndk_sp = self._parse_lib_list(
84            os.path.basename(self._etc_paths['vndksp.libraries.txt']))
85        self._vndk_private = self._parse_lib_list('vndkprivate.libraries.txt')
86        self._modules_with_notice = self._get_modules_with_notice()
87
88    def _get_etc_paths(self):
89        """Returns a map of relative file paths for each ETC module."""
90
91        etc_paths = dict()
92        for etc_module in self.ETC_MODULES:
93            etc_pattern = '{}*'.format(os.path.splitext(etc_module)[0])
94            etc_path = glob.glob(
95                os.path.join(self._install_dir, utils.CONFIG_DIR_PATH_PATTERN,
96                             etc_pattern))[0]
97            rel_etc_path = etc_path.replace(self._install_dir, '')[1:]
98            etc_paths[etc_module] = rel_etc_path
99        return etc_paths
100
101    def _parse_lib_list(self, txt_filename):
102        """Returns a map of VNDK library lists per VNDK snapshot arch.
103
104        Args:
105          txt_filename: string, name of snapshot config file
106
107        Returns:
108          dict, e.g. {'arm64': ['libfoo.so', 'libbar.so', ...], ...}
109        """
110        lib_map = dict()
111        for txt_path in utils.find(self._install_dir, [txt_filename]):
112            arch = utils.snapshot_arch_from_path(txt_path)
113            abs_path_of_txt = os.path.join(self._install_dir, txt_path)
114            with open(abs_path_of_txt, 'r') as f:
115                lib_map[arch] = f.read().strip().split('\n')
116        return lib_map
117
118    def _get_modules_with_notice(self):
119        """Returns a list of modules that have associated notice files. """
120        notice_paths = glob.glob(
121            os.path.join(self._install_dir, utils.NOTICE_FILES_DIR_PATH,
122                         '*.txt'))
123        return [os.path.splitext(os.path.basename(p))[0] for p in notice_paths]
124
125    def generate_root_android_bp(self):
126        """Autogenerates Android.bp."""
127
128        logging.info('Generating Android.bp for snapshot v{}'.format(
129            self._vndk_version))
130        etc_buildrules = []
131        for prebuilt in self.ETC_MODULES:
132            # ld.config.VER.txt is not installed as a prebuilt but is built and
133            # installed from thesource tree at the time the VNDK snapshot is
134            # installed to the system.img.
135            if prebuilt == 'ld.config.txt':
136                continue
137            etc_buildrules.append(self._gen_etc_prebuilt(prebuilt))
138
139        with open(self._root_bpfile, 'w') as bpfile:
140            bpfile.write(self._gen_autogen_msg('/'))
141            bpfile.write('\n')
142            bpfile.write('\n'.join(etc_buildrules))
143            bpfile.write('\n')
144
145        logging.info('Successfully generated {}'.format(self._root_bpfile))
146
147    def generate_common_android_bp(self):
148        """Autogenerates common/Android.bp."""
149
150        logging.info('Generating common/Android.bp for snapshot v{}'.format(
151            self._vndk_version))
152        with open(self._common_bpfile, 'w') as bpfile:
153            bpfile.write(self._gen_autogen_msg('/'))
154            for module in self._modules_with_notice:
155                bpfile.write('\n')
156                bpfile.write(self._gen_notice_filegroup(module))
157
158    def generate_android_bp(self):
159        """Autogenerates Android.bp."""
160
161        def gen_for_variant(arch, is_binder32=False):
162            """Generates Android.bp file for specified VNDK snapshot variant.
163
164            A VNDK snapshot variant is defined by the TARGET_ARCH and binder
165            bitness. Example snapshot variants:
166                vndk_v{ver}_arm:            {arch: arm, binder: 64-bit}
167                vndk_v{ver}_arm_binder32:   {arch: arm, binder: 32-bit}
168
169            Args:
170              arch: string, VNDK snapshot arch (e.g. 'arm64')
171              is_binder32: bool, True if binder interface is 32-bit
172            """
173            binder32_suffix = '_{}'.format(
174                utils.BINDER32) if is_binder32 else ''
175            logging.info('Generating Android.bp for vndk_v{}_{}{}'.format(
176                self._vndk_version, arch, binder32_suffix))
177
178            variant_subpath = arch
179            # For O-MR1 snapshot (v27), 32-bit binder prebuilts are not
180            # isolated in separate 'binder32' subdirectory.
181            if is_binder32 and self._vndk_version >= 28:
182                variant_subpath = os.path.join(arch, utils.BINDER32)
183            bpfile_path = os.path.join(self._install_dir, variant_subpath,
184                                       'Android.bp')
185
186            vndk_core_buildrules = self._gen_vndk_shared_prebuilts(
187                self._vndk_core[arch], arch, is_binder32=is_binder32)
188            vndk_sp_buildrules = self._gen_vndk_shared_prebuilts(
189                self._vndk_sp[arch],
190                arch,
191                is_vndk_sp=True,
192                is_binder32=is_binder32)
193
194            with open(bpfile_path, 'w') as bpfile:
195                bpfile.write(self._gen_autogen_msg('/'))
196                bpfile.write('\n')
197                bpfile.write(self._gen_bp_phony(arch, is_binder32))
198                bpfile.write('\n')
199                bpfile.write('\n'.join(vndk_core_buildrules))
200                bpfile.write('\n')
201                bpfile.write('\n'.join(vndk_sp_buildrules))
202
203            logging.info('Successfully generated {}'.format(bpfile_path))
204
205        if self._vndk_version == 27:
206            # For O-MR1 snapshot (v27), 32-bit binder prebuilts are not
207            # isolated in separate 'binder32' subdirectory.
208            for arch in self._snapshot_archs:
209                if arch in ('arm', 'x86'):
210                    gen_for_variant(arch, is_binder32=True)
211                else:
212                    gen_for_variant(arch)
213            return
214
215        for arch in self._snapshot_archs:
216            if os.path.isdir(
217                    os.path.join(self._install_dir, arch, utils.BINDER32)):
218                gen_for_variant(arch, is_binder32=True)
219            gen_for_variant(arch)
220
221    def _gen_autogen_msg(self, comment_char):
222        return ('{0}{0} THIS FILE IS AUTOGENERATED BY '
223                'development/vndk/snapshot/gen_buildfiles.py\n'
224                '{0}{0} DO NOT EDIT\n'.format(comment_char))
225
226    def _get_versioned_name(self,
227                            prebuilt,
228                            arch,
229                            is_etc=False,
230                            is_binder32=False):
231        """Returns the VNDK version-specific module name for a given prebuilt.
232
233        The VNDK version-specific module name is defined as follows:
234        For a VNDK shared lib: 'libfoo.so'
235            if binder is 32-bit:
236                'libfoo.vndk.{version}.{arch}.binder32.vendor'
237            else:
238                'libfoo.vndk.{version}.{arch}.vendor'
239        For an ETC module: 'foo.txt' -> 'foo.{version}.txt'
240
241        Args:
242          prebuilt: string, name of the prebuilt object
243          arch: string, VNDK snapshot arch (e.g. 'arm64')
244          is_etc: bool, True if the LOCAL_MODULE_CLASS of prebuilt is 'ETC'
245          is_binder32: bool, True if binder interface is 32-bit
246        """
247        name, ext = os.path.splitext(prebuilt)
248        if is_etc:
249            versioned_name = '{}.{}{}'.format(name, self._vndk_version, ext)
250        else:
251            binder_suffix = '.{}'.format(utils.BINDER32) if is_binder32 else ''
252            versioned_name = '{}.vndk.{}.{}{}.vendor'.format(
253                name, self._vndk_version, arch, binder_suffix)
254
255        return versioned_name
256
257    def _gen_etc_prebuilt(self, prebuilt):
258        """Generates build rule for an ETC prebuilt.
259
260        Args:
261          prebuilt: string, name of ETC prebuilt object
262        """
263        etc_path = self._etc_paths[prebuilt]
264        etc_sub_path = etc_path[etc_path.index('/') + 1:]
265
266        prebuilt_etc = ('prebuilt_etc {{\n'
267                        '{ind}name: "{versioned_name}",\n'
268                        '{ind}target: {{\n'.format(
269                            ind=self.INDENT,
270                            versioned_name=self._get_versioned_name(
271                                prebuilt, None, is_etc=True)))
272        for arch in self._snapshot_archs:
273            prebuilt_etc += ('{ind}{ind}android_{arch}: {{\n'
274                             '{ind}{ind}{ind}src: "{arch}/{etc_sub_path}",\n'
275                             '{ind}{ind}}},\n'.format(
276                                 ind=self.INDENT,
277                                 arch=arch,
278                                 etc_sub_path=etc_sub_path))
279        prebuilt_etc += ('{ind}}},\n'
280                         '}}\n'.format(ind=self.INDENT))
281        return prebuilt_etc
282
283    def _gen_notice_filegroup(self, module):
284        """Generates a notice filegroup build rule for a given module.
285
286        Args:
287          notice: string, module name
288        """
289        return ('filegroup {{\n'
290                '{ind}name: "{filegroup_name}",\n'
291                '{ind}srcs: ["{notice_dir}/{module}.txt"],\n'
292                '}}\n'.format(
293                    ind=self.INDENT,
294                    filegroup_name=self._get_notice_filegroup_name(module),
295                    module=module,
296                    notice_dir=utils.NOTICE_FILES_DIR_NAME))
297
298    def _get_notice_filegroup_name(self, module):
299        """ Gets a notice filegroup module name for a given module.
300
301        Args:
302          notice: string, module name.
303        """
304        return 'vndk-v{ver}-{module}-notice'.format(
305            ver=self._vndk_version, module=module)
306
307    def _gen_bp_phony(self, arch, is_binder32=False):
308        """Generates build rule for phony package 'vndk_v{ver}_{arch}'.
309
310        Args:
311          arch: string, VNDK snapshot arch (e.g. 'arm64')
312          is_binder32: bool, True if binder interface is 32-bit
313        """
314        required = []
315        for prebuilts in (self._vndk_core[arch], self._vndk_sp[arch]):
316            for prebuilt in prebuilts:
317                required.append(
318                    self._get_versioned_name(
319                        prebuilt, arch, is_binder32=is_binder32))
320
321        for prebuilt in self.ETC_MODULES:
322            required.append(
323                self._get_versioned_name(
324                    prebuilt, None, is_etc=True, is_binder32=is_binder32))
325
326        required_str = ['"{}",'.format(prebuilt) for prebuilt in required]
327        required_formatted = '\n{ind}{ind}'.format(
328            ind=self.INDENT).join(required_str)
329        required_buildrule = ('{ind}required: [\n'
330                              '{ind}{ind}{required_formatted}\n'
331                              '{ind}],\n'.format(
332                                  ind=self.INDENT,
333                                  required_formatted=required_formatted))
334        binder_suffix = '_{}'.format(utils.BINDER32) if is_binder32 else ''
335
336        return ('phony {{\n'
337                '{ind}name: "vndk_v{ver}_{arch}{binder_suffix}",\n'
338                '{required_buildrule}'
339                '}}\n'.format(
340                    ind=self.INDENT,
341                    ver=self._vndk_version,
342                    arch=arch,
343                    binder_suffix=binder_suffix,
344                    required_buildrule=required_buildrule))
345
346    def _gen_vndk_shared_prebuilts(self,
347                                   prebuilts,
348                                   arch,
349                                   is_vndk_sp=False,
350                                   is_binder32=False):
351        """Returns list of build rules for given prebuilts.
352
353        Args:
354          prebuilts: list of VNDK shared prebuilts
355          arch: string, VNDK snapshot arch (e.g. 'arm64')
356          is_vndk_sp: bool, True if prebuilts are VNDK_SP libs
357          is_binder32: bool, True if binder interface is 32-bit
358        """
359        build_rules = []
360        for prebuilt in prebuilts:
361            build_rules.append(
362                self._gen_vndk_shared_prebuilt(
363                    prebuilt,
364                    arch,
365                    is_vndk_sp=is_vndk_sp,
366                    is_binder32=is_binder32))
367        return build_rules
368
369    def _gen_vndk_shared_prebuilt(self,
370                                  prebuilt,
371                                  arch,
372                                  is_vndk_sp=False,
373                                  is_binder32=False):
374        """Returns build rule for given prebuilt.
375
376        Args:
377          prebuilt: string, name of prebuilt object
378          arch: string, VNDK snapshot arch (e.g. 'arm64')
379          is_vndk_sp: bool, True if prebuilt is a VNDK_SP lib
380          is_binder32: bool, True if binder interface is 32-bit
381        """
382
383        def get_notice_file(prebuilt):
384            """Returns build rule for notice file (attribute 'notice').
385
386            Args:
387              prebuilt: string, name of prebuilt object
388            """
389            notice = ''
390            if prebuilt in self._modules_with_notice:
391                notice = '{ind}notice: ":{notice_filegroup}",\n'.format(
392                    ind=self.INDENT,
393                    notice_filegroup=self._get_notice_filegroup_name(prebuilt))
394            return notice
395
396        def get_rel_install_path(prebuilt):
397            """Returns build rule for 'relative_install_path'.
398
399            Args:
400              prebuilt: string, name of prebuilt object
401            """
402            rel_install_path = ''
403            if prebuilt in self.RELATIVE_INSTALL_PATHS:
404                path = self.RELATIVE_INSTALL_PATHS[prebuilt]
405                rel_install_path += ('{ind}relative_install_path: "{path}",\n'
406                                     .format(ind=self.INDENT, path=path))
407            return rel_install_path
408
409        def get_arch_srcs(prebuilt, arch):
410            """Returns build rule for arch specific srcs.
411
412            e.g.,
413                arch: {
414                    arm: {
415                        srcs: ["..."]
416                    },
417                    arm64: {
418                        srcs: ["..."]
419                    },
420                }
421
422            Args:
423              prebuilt: string, name of prebuilt object
424              arch: string, VNDK snapshot arch (e.g. 'arm64')
425            """
426            arch_srcs = '{ind}arch: {{\n'.format(ind=self.INDENT)
427            src_paths = utils.find(src_root, [prebuilt])
428            # filter out paths under 'binder32' subdirectory
429            src_paths = filter(lambda src: not src.startswith(utils.BINDER32),
430                               src_paths)
431
432            for src in sorted(src_paths):
433                arch_srcs += ('{ind}{ind}{arch}: {{\n'
434                              '{ind}{ind}{ind}srcs: ["{src}"],\n'
435                              '{ind}{ind}}},\n'.format(
436                                  ind=self.INDENT,
437                                  arch=utils.prebuilt_arch_from_path(
438                                      os.path.join(arch, src)),
439                                  src=src))
440            arch_srcs += '{ind}}},\n'.format(ind=self.INDENT)
441            return arch_srcs
442
443        src_root = os.path.join(self._install_dir, arch)
444        # For O-MR1 snapshot (v27), 32-bit binder prebuilts are not
445        # isolated in separate 'binder32' subdirectory.
446        if is_binder32 and self._vndk_version >= 28:
447            src_root = os.path.join(src_root, utils.BINDER32)
448
449        name = os.path.splitext(prebuilt)[0]
450        vendor_available = str(
451            prebuilt not in self._vndk_private[arch]).lower()
452
453        vndk_sp = ''
454        if is_vndk_sp:
455            vndk_sp = '{ind}{ind}support_system_process: true,\n'.format(
456                ind=self.INDENT)
457
458        notice = get_notice_file(prebuilt)
459        rel_install_path = get_rel_install_path(prebuilt)
460        arch_srcs = get_arch_srcs(prebuilt, arch)
461
462        binder32bit = ''
463        if is_binder32:
464            binder32bit = '{ind}binder32bit: true,\n'.format(ind=self.INDENT)
465
466        return ('vndk_prebuilt_shared {{\n'
467                '{ind}name: "{name}",\n'
468                '{ind}version: "{ver}",\n'
469                '{ind}target_arch: "{target_arch}",\n'
470                '{binder32bit}'
471                '{ind}vendor_available: {vendor_available},\n'
472                '{ind}vndk: {{\n'
473                '{ind}{ind}enabled: true,\n'
474                '{vndk_sp}'
475                '{ind}}},\n'
476                '{notice}'
477                '{rel_install_path}'
478                '{arch_srcs}'
479                '}}\n'.format(
480                    ind=self.INDENT,
481                    name=name,
482                    ver=self._vndk_version,
483                    target_arch=arch,
484                    binder32bit=binder32bit,
485                    vendor_available=vendor_available,
486                    vndk_sp=vndk_sp,
487                    notice=notice,
488                    rel_install_path=rel_install_path,
489                    arch_srcs=arch_srcs))
490
491
492def get_args():
493    parser = argparse.ArgumentParser()
494    parser.add_argument(
495        'vndk_version',
496        type=int,
497        help='VNDK snapshot version to install, e.g. "27".')
498    parser.add_argument(
499        '-v',
500        '--verbose',
501        action='count',
502        default=0,
503        help='Increase output verbosity, e.g. "-v", "-vv".')
504    return parser.parse_args()
505
506
507def main():
508    """For local testing purposes.
509
510    Note: VNDK snapshot must be already installed under
511      prebuilts/vndk/v{version}.
512    """
513    ANDROID_BUILD_TOP = utils.get_android_build_top()
514    PREBUILTS_VNDK_DIR = utils.join_realpath(ANDROID_BUILD_TOP,
515                                             'prebuilts/vndk')
516
517    args = get_args()
518    vndk_version = args.vndk_version
519    install_dir = os.path.join(PREBUILTS_VNDK_DIR, 'v{}'.format(vndk_version))
520    if not os.path.isdir(install_dir):
521        raise ValueError(
522            'Please provide valid VNDK version. {} does not exist.'
523            .format(install_dir))
524    utils.set_logging_config(args.verbose)
525
526    buildfile_generator = GenBuildFile(install_dir, vndk_version)
527    buildfile_generator.generate_root_android_bp()
528    buildfile_generator.generate_common_android_bp()
529    buildfile_generator.generate_android_bp()
530
531    logging.info('Done.')
532
533
534if __name__ == '__main__':
535    main()
536