1#!/usr/bin/env python 2# 3# Copyright (C) 2016 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"""Clobbers all the per-API level headers with the current unified headers. 18 19This is just for testing purposes. Having unified headers isn't worth as much 20if we're still shipping N copies of them :) 21""" 22from __future__ import absolute_import 23from __future__ import print_function 24 25import logging 26import os 27import shutil 28 29 30THIS_DIR = os.path.realpath(os.path.dirname(__file__)) 31ANDROID_ROOT = os.path.dirname(os.path.dirname(THIS_DIR)) 32 33 34ALL_ARCHITECTURES = ( 35 'arm', 36 'arm64', 37 'mips', 38 'mips64', 39 'x86', 40 'x86_64', 41) 42 43 44def logger(): 45 """Return the main logger for this module.""" 46 return logging.getLogger(__name__) 47 48 49def android_path(*args): 50 """Returns a full path from the base of the Android source tree.""" 51 return os.path.join(ANDROID_ROOT, *args) 52 53 54def copy_directory_contents(src, dst): 55 """Copies the contents of a directory, merging with the destination. 56 57 shutil.copytree requires that the destination does not exist. This function 58 behaves like `cp -r`. That is, it merges the source and destination 59 directories if appropriate. 60 """ 61 for root, dirs, files in os.walk(src): 62 subdir = os.path.relpath(root, src) 63 dst_dir = os.path.join(dst, subdir) 64 if not os.path.exists(dst_dir): 65 os.makedirs(dst_dir) 66 67 # This makes sure we copy even empty directories. We don't actually 68 # need it, but for now it lets us diff between our result and the 69 # legacy tool. 70 for d in dirs: # pylint: disable=invalid-name 71 d_path = os.path.join(root, d) 72 if os.path.islink(d_path): 73 linkto = os.readlink(d_path) 74 dst_file = os.path.join(dst_dir, d) 75 logger().debug('Symlinking %s to %s', dst_file, linkto) 76 os.symlink(linkto, dst_file) 77 else: 78 new_dir = os.path.join(dst_dir, d) 79 if not os.path.exists(new_dir): 80 logger().debug('Making directory %s', new_dir) 81 os.makedirs(new_dir) 82 83 for f in files: # pylint: disable=invalid-name 84 src_file = os.path.join(root, f) 85 if os.path.islink(src_file): 86 linkto = os.readlink(src_file) 87 dst_file = os.path.join(dst_dir, f) 88 logger().debug('Symlinking %s to %s', dst_file, linkto) 89 os.symlink(linkto, dst_file) 90 else: 91 logger().debug('Copying %s', src_file) 92 shutil.copy2(src_file, dst_dir) 93 94 95def get_preserve_path(platform_dir): 96 """Returns the path used for preserving headers.""" 97 return os.path.join(platform_dir, 'preserve') 98 99 100def preserve_headers(keep_list, platform_dir): 101 """Preserves a list of headers to be restored after the copy.""" 102 install_path = os.path.join(platform_dir, 'include') 103 preserve_root = get_preserve_path(platform_dir) 104 if os.path.exists(preserve_root): 105 shutil.rmtree(preserve_root) 106 for preserve in keep_list: 107 path = os.path.join(install_path, preserve) 108 if os.path.isdir(path): 109 shutil.copytree(path, os.path.join(preserve_root, preserve)) 110 elif os.path.isfile(path): 111 shutil.copy2(path, preserve_root) 112 113 114def restore_headers(keep_list, platform_dir): 115 """Restores headers preserved by preserve_headers.""" 116 install_path = os.path.join(platform_dir, 'include') 117 preserve_root = get_preserve_path(platform_dir) 118 for preserve in keep_list: 119 path = os.path.join(preserve_root, preserve) 120 if os.path.isdir(path): 121 # Bionic does have include/android, so we need to merge directories 122 # here. 123 copy_directory_contents( 124 path, os.path.join(install_path, preserve)) 125 elif os.path.isfile(path): 126 shutil.copy2(path, install_path) 127 128 129def platform_path_to_api_level(platform_path): 130 """Returns the API level for a platform path.""" 131 basename = os.path.split(platform_path)[-1] 132 # android-\d+ 133 return int(basename.split('-')[1]) 134 135 136def fixup_api_level_h(platform_dir): 137 """Rewrites api-level.h for the correct platform.""" 138 api_level = platform_path_to_api_level(platform_dir) 139 header = os.path.join(platform_dir, 'include/android/api-level.h') 140 with open(header, 'w') as header_file: 141 header_file.write( 142 '#ifndef ANDROID_API_LEVEL_H\n' 143 '#define ANDROID_API_LEVEL_H\n' 144 '#ifndef __ANDROID_API__\n' 145 '#define __ANDROID_API__ {}\n' 146 '#endif /* __ANDROID_API__ */\n' 147 '#endif /* ANDROID_API_LEVEL_H */\n'.format(api_level)) 148 149 150def copy_current_headers(platform_dir): 151 """Copies the unified headers into a per-API directory.""" 152 libc_path = android_path('bionic/libc') 153 install_path = os.path.join(platform_dir, 'include') 154 155 # None of the platform APIs have unified headers yet. Copy those out of the 156 # way of the purge and copy them back in afterward. 157 keep_list = ( 158 'EGL', 159 'GLES', 160 'GLES2', 161 'GLES3', 162 'KHR', 163 'OMXAL', 164 'SLES', 165 'android', 166 'camera', 167 'jni.h', 168 'media', 169 'vulkan', 170 'zlib.h', 171 'zconf.h', 172 ) 173 preserve_headers(keep_list, platform_dir) 174 175 if os.path.exists(install_path): 176 shutil.rmtree(install_path) 177 178 shutil.copytree(os.path.join(libc_path, 'include'), install_path) 179 shutil.copytree(os.path.join(libc_path, 'kernel/uapi/linux'), 180 os.path.join(install_path, 'linux')) 181 shutil.copytree(os.path.join(libc_path, 'kernel/uapi/asm-generic'), 182 os.path.join(install_path, 'asm-generic')) 183 shutil.copy2(os.path.join(libc_path, 'NOTICE'), install_path) 184 185 restore_headers(keep_list, platform_dir) 186 187 if os.path.exists(get_preserve_path(platform_dir)): 188 shutil.rmtree(get_preserve_path(platform_dir)) 189 190 fixup_api_level_h(platform_dir) 191 192 api_level = platform_path_to_api_level(platform_dir) 193 for arch in ALL_ARCHITECTURES: 194 if arch in ('arm64', 'mips64', 'x86_64') and api_level < 21: 195 continue 196 197 # For multilib architectures we need to copy the 32-bit headers into 198 # the 64-bit directory. For MIPS this is true of asm and machine, for 199 # Intel it's only for asm. 200 asm_arch = arch 201 machine_arch = arch 202 if arch == 'mips64': 203 asm_arch = 'mips' 204 machine_arch = 'mips' 205 elif arch == 'x86_64': 206 asm_arch = 'x86' 207 208 arch_install_path = os.path.join( 209 platform_dir, 'arch-' + arch, 'include') 210 if os.path.exists(arch_install_path): 211 shutil.rmtree(arch_install_path) 212 213 machine_path = os.path.join( 214 libc_path, 'arch-' + machine_arch, 'include/machine') 215 asm_path = os.path.join( 216 libc_path, 'kernel/uapi/asm-' + asm_arch, 'asm') 217 218 if os.path.exists(arch_install_path): 219 shutil.rmtree(arch_install_path) 220 os.makedirs(arch_install_path) 221 222 shutil.copytree( 223 machine_path, os.path.join(arch_install_path, 'machine')) 224 shutil.copytree(asm_path, os.path.join(arch_install_path, 'asm')) 225 226 227def main(): 228 """Program entry point.""" 229 platforms_dir = os.path.join(THIS_DIR, 'platforms') 230 for platform_dir in os.listdir(platforms_dir): 231 path = os.path.join(platforms_dir, platform_dir) 232 if not os.path.isdir(path): 233 continue 234 if platform_dir == 'common': 235 # Just contains crtbrand. 236 continue 237 copy_current_headers(path) 238 239 240if __name__ == '__main__': 241 main() 242