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