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 glob 19import os 20import sys 21 22import utils 23 24 25class GenBuildFile(object): 26 """Generates Android.mk and Android.bp for VNDK snapshot. 27 28 VNDK snapshot directory structure under prebuilts/vndk/v{version}: 29 {SNAPSHOT_VARIANT}/ 30 Android.bp 31 arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/ 32 shared/ 33 vndk-core/ 34 (VNDK-core libraries, e.g. libbinder.so) 35 vndk-sp/ 36 (VNDK-SP libraries, e.g. libc++.so) 37 arch-{TARGET_2ND_ARCH}-{TARGET_2ND_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 configs/ 44 (various *.txt configuration files, e.g. ld.config.*.txt) 45 ... (other {SNAPSHOT_VARIANT}/ directories) 46 common/ 47 Android.mk 48 NOTICE_FILES/ 49 (license files, e.g. libfoo.so.txt) 50 """ 51 INDENT = ' ' 52 ETC_MODULES = [ 53 'ld.config.txt', 'llndk.libraries.txt', 'vndksp.libraries.txt' 54 ] 55 56 # TODO(b/70312118): Parse from soong build system 57 RELATIVE_INSTALL_PATHS = {'android.hidl.memory@1.0-impl.so': 'hw'} 58 59 def __init__(self, install_dir, vndk_version): 60 """GenBuildFile constructor. 61 62 Args: 63 install_dir: string, absolute path to the prebuilts/vndk/v{version} 64 directory where the build files will be generated. 65 vndk_version: int, VNDK snapshot version (e.g., 27, 28) 66 """ 67 self._install_dir = install_dir 68 self._vndk_version = vndk_version 69 self._etc_paths = self._get_etc_paths() 70 self._snapshot_variants = utils.get_snapshot_variants(install_dir) 71 self._mkfile = os.path.join(install_dir, utils.ANDROID_MK_PATH) 72 self._vndk_core = self._parse_lib_list('vndkcore.libraries.txt') 73 self._vndk_sp = self._parse_lib_list( 74 os.path.basename(self._etc_paths['vndksp.libraries.txt'])) 75 self._vndk_private = self._parse_lib_list('vndkprivate.libraries.txt') 76 77 def _get_etc_paths(self): 78 """Returns a map of relative file paths for each ETC module.""" 79 80 etc_paths = dict() 81 for etc_module in self.ETC_MODULES: 82 etc_pattern = '{}*'.format(os.path.splitext(etc_module)[0]) 83 etc_path = glob.glob( 84 os.path.join(self._install_dir, utils.CONFIG_DIR_PATH_PATTERN, 85 etc_pattern))[0] 86 rel_etc_path = etc_path.replace(self._install_dir, '')[1:] 87 etc_paths[etc_module] = rel_etc_path 88 return etc_paths 89 90 def _parse_lib_list(self, txt_filename): 91 """Returns a map of VNDK library lists per VNDK snapshot variant. 92 93 Args: 94 txt_filename: string, name of snapshot config file 95 96 Returns: 97 dict, e.g. {'arm64': ['libfoo.so', 'libbar.so', ...], ...} 98 """ 99 lib_map = dict() 100 for txt_path in utils.find(self._install_dir, [txt_filename]): 101 variant = utils.variant_from_path(txt_path) 102 abs_path_of_txt = os.path.join(self._install_dir, txt_path) 103 with open(abs_path_of_txt, 'r') as f: 104 lib_map[variant] = f.read().strip().split('\n') 105 return lib_map 106 107 def generate_android_mk(self): 108 """Autogenerates Android.mk.""" 109 110 etc_buildrules = [] 111 for prebuilt in self.ETC_MODULES: 112 etc_buildrules.append(self._gen_etc_prebuilt(prebuilt)) 113 114 with open(self._mkfile, 'w') as mkfile: 115 mkfile.write(self._gen_autogen_msg('#')) 116 mkfile.write('\n') 117 mkfile.write('LOCAL_PATH := $(call my-dir)\n') 118 mkfile.write('\n') 119 mkfile.write('\n\n'.join(etc_buildrules)) 120 mkfile.write('\n') 121 122 def generate_android_bp(self): 123 """Autogenerates Android.bp file for each VNDK snapshot variant.""" 124 125 for variant in self._snapshot_variants: 126 bpfile = os.path.join(self._install_dir, variant, 'Android.bp') 127 vndk_core_buildrules = self._gen_vndk_shared_prebuilts( 128 self._vndk_core[variant], variant, False) 129 vndk_sp_buildrules = self._gen_vndk_shared_prebuilts( 130 self._vndk_sp[variant], variant, True) 131 132 with open(bpfile, 'w') as bpfile: 133 bpfile.write(self._gen_autogen_msg('/')) 134 bpfile.write('\n') 135 bpfile.write(self._gen_bp_phony(variant)) 136 bpfile.write('\n') 137 bpfile.write('\n'.join(vndk_core_buildrules)) 138 bpfile.write('\n') 139 bpfile.write('\n'.join(vndk_sp_buildrules)) 140 141 def _gen_autogen_msg(self, comment_char): 142 return ('{0}{0} THIS FILE IS AUTOGENERATED BY ' 143 'development/vndk/snapshot/gen_buildfiles.py\n' 144 '{0}{0} DO NOT EDIT\n'.format(comment_char)) 145 146 def _get_versioned_name(self, prebuilt, variant, is_etc): 147 """Returns the VNDK version-specific module name for a given prebuilt. 148 149 The VNDK version-specific module name is defined as follows: 150 For a VNDK shared lib: 'libfoo.so' 151 -> 'libfoo.vndk.{version}.{variant}.vendor' 152 For an ETC module: 'foo.txt' -> 'foo.{version}.txt' 153 154 Args: 155 prebuilt: string, name of the prebuilt object 156 variant: string, VNDK snapshot variant (e.g. 'arm64') 157 is_etc: bool, True if the LOCAL_MODULE_CLASS of prebuilt is 'ETC' 158 """ 159 name, ext = os.path.splitext(prebuilt) 160 if is_etc: 161 versioned_name = '{}.{}{}'.format(name, self._vndk_version, ext) 162 else: 163 versioned_name = '{}.vndk.{}.{}.vendor'.format( 164 name, self._vndk_version, variant) 165 166 return versioned_name 167 168 def _gen_etc_prebuilt(self, prebuilt): 169 """Generates build rule for an ETC prebuilt. 170 171 Args: 172 prebuilt: string, name of ETC prebuilt object 173 """ 174 etc_path = self._etc_paths[prebuilt] 175 etc_sub_path = etc_path[etc_path.index('/') + 1:] 176 177 return ('#######################################\n' 178 '# {prebuilt}\n' 179 'include $(CLEAR_VARS)\n' 180 'LOCAL_MODULE := {versioned_name}\n' 181 'LOCAL_SRC_FILES := ../$(TARGET_ARCH)/{etc_sub_path}\n' 182 'LOCAL_MODULE_CLASS := ETC\n' 183 'LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)\n' 184 'LOCAL_MODULE_STEM := $(LOCAL_MODULE)\n' 185 'include $(BUILD_PREBUILT)\n'.format( 186 prebuilt=prebuilt, 187 versioned_name=self._get_versioned_name( 188 prebuilt, None, True), 189 etc_sub_path=etc_sub_path)) 190 191 def _gen_bp_phony(self, variant): 192 """Generates build rule for phony package 'vndk_v{ver}_{variant}'. 193 194 Args: 195 variant: string, VNDK snapshot variant (e.g. 'arm64') 196 """ 197 required = [] 198 for prebuilts in (self._vndk_core[variant], self._vndk_sp[variant]): 199 for prebuilt in prebuilts: 200 required.append( 201 self._get_versioned_name(prebuilt, variant, False)) 202 203 for prebuilt in self.ETC_MODULES: 204 required.append(self._get_versioned_name(prebuilt, None, True)) 205 206 required_str = ['"{}",'.format(prebuilt) for prebuilt in required] 207 required_formatted = '\n{ind}{ind}'.format( 208 ind=self.INDENT).join(required_str) 209 required_buildrule = ('{ind}required: [\n' 210 '{ind}{ind}{required_formatted}\n' 211 '{ind}],\n'.format( 212 ind=self.INDENT, 213 required_formatted=required_formatted)) 214 215 return ('phony {{\n' 216 '{ind}name: "vndk_v{ver}_{variant}",\n' 217 '{required_buildrule}' 218 '}}\n'.format( 219 ind=self.INDENT, 220 ver=self._vndk_version, 221 variant=variant, 222 required_buildrule=required_buildrule)) 223 224 def _gen_vndk_shared_prebuilts(self, prebuilts, variant, is_vndk_sp): 225 """Returns list of build rules for given prebuilts. 226 227 Args: 228 prebuilts: list of VNDK shared prebuilts 229 variant: string, VNDK snapshot variant (e.g. 'arm64') 230 is_vndk_sp: bool, True if prebuilts are VNDK_SP libs 231 """ 232 build_rules = [] 233 for prebuilt in prebuilts: 234 build_rules.append( 235 self._gen_vndk_shared_prebuilt(prebuilt, variant, is_vndk_sp)) 236 return build_rules 237 238 def _gen_vndk_shared_prebuilt(self, prebuilt, variant, is_vndk_sp): 239 """Returns build rule for given prebuilt. 240 241 Args: 242 prebuilt: string, name of prebuilt object 243 variant: string, VNDK snapshot variant (e.g. 'arm64') 244 is_vndk_sp: bool, True if prebuilt is a VNDK_SP lib 245 """ 246 247 def get_notice_file(prebuilt): 248 """Returns build rule for notice file (attribute 'notice'). 249 250 Args: 251 prebuilt: string, name of prebuilt object 252 """ 253 notice = '' 254 notice_file_name = '{}.txt'.format(prebuilt) 255 notices_dir = os.path.join(self._install_dir, 256 utils.NOTICE_FILES_DIR_PATH) 257 notice_files = utils.find(notices_dir, [notice_file_name]) 258 if len(notice_files) > 0: 259 notice = '{ind}notice: "{notice_file_path}",\n'.format( 260 ind=self.INDENT, 261 notice_file_path=os.path.join( 262 '..', utils.NOTICE_FILES_DIR_PATH, notice_files[0])) 263 return notice 264 265 def get_rel_install_path(prebuilt): 266 """Returns build rule for 'relative_install_path'. 267 268 Args: 269 prebuilt: string, name of prebuilt object 270 """ 271 rel_install_path = '' 272 if prebuilt in self.RELATIVE_INSTALL_PATHS: 273 path = self.RELATIVE_INSTALL_PATHS[prebuilt] 274 rel_install_path += ('{ind}relative_install_path: "{path}",\n' 275 .format(ind=self.INDENT, path=path)) 276 return rel_install_path 277 278 def get_arch_srcs(prebuilt, variant): 279 """Returns build rule for arch specific srcs. 280 281 e.g., 282 arch: { 283 arm: { 284 srcs: ["..."] 285 }, 286 arm64: { 287 srcs: ["..."] 288 }, 289 } 290 291 Args: 292 prebuilt: string, name of prebuilt object 293 variant: string, VNDK snapshot variant (e.g. 'arm64') 294 """ 295 arch_srcs = '{ind}arch: {{\n'.format(ind=self.INDENT) 296 variant_path = os.path.join(self._install_dir, variant) 297 src_paths = utils.find(variant_path, [prebuilt]) 298 for src in sorted(src_paths): 299 arch_srcs += ('{ind}{ind}{arch}: {{\n' 300 '{ind}{ind}{ind}srcs: ["{src}"],\n' 301 '{ind}{ind}}},\n'.format( 302 ind=self.INDENT, 303 arch=utils.arch_from_path( 304 os.path.join(variant, src)), 305 src=src)) 306 arch_srcs += '{ind}}},\n'.format(ind=self.INDENT) 307 return arch_srcs 308 309 name = os.path.splitext(prebuilt)[0] 310 vendor_available = str( 311 prebuilt not in self._vndk_private[variant]).lower() 312 if is_vndk_sp: 313 vndk_sp = '{ind}{ind}support_system_process: true,\n'.format( 314 ind=self.INDENT) 315 else: 316 vndk_sp = '' 317 notice = get_notice_file(prebuilt) 318 rel_install_path = get_rel_install_path(prebuilt) 319 arch_srcs = get_arch_srcs(prebuilt, variant) 320 321 return ('vndk_prebuilt_shared {{\n' 322 '{ind}name: "{name}",\n' 323 '{ind}version: "{ver}",\n' 324 '{ind}target_arch: "{target_arch}",\n' 325 '{ind}vendor_available: {vendor_available},\n' 326 '{ind}vndk: {{\n' 327 '{ind}{ind}enabled: true,\n' 328 '{vndk_sp}' 329 '{ind}}},\n' 330 '{notice}' 331 '{rel_install_path}' 332 '{arch_srcs}' 333 '}}\n'.format( 334 ind=self.INDENT, 335 name=name, 336 ver=self._vndk_version, 337 vendor_available=vendor_available, 338 target_arch=variant, 339 vndk_sp=vndk_sp, 340 notice=notice, 341 rel_install_path=rel_install_path, 342 arch_srcs=arch_srcs)) 343 344 345def main(): 346 """For local testing purposes. 347 348 Note: VNDK snapshot must be already installed under 349 prebuilts/vndk/v{version}. 350 """ 351 ANDROID_BUILD_TOP = utils.get_android_build_top() 352 PREBUILTS_VNDK_DIR = utils.join_realpath(ANDROID_BUILD_TOP, 353 'prebuilts/vndk') 354 355 vndk_version = 27 # set appropriately 356 install_dir = os.path.join(PREBUILTS_VNDK_DIR, 'v{}'.format(vndk_version)) 357 358 buildfile_generator = GenBuildFile(install_dir, vndk_version) 359 buildfile_generator.generate_android_mk() 360 buildfile_generator.generate_android_bp() 361 362 363if __name__ == '__main__': 364 main() 365