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