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