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 """Installs VNDK snapshot under prebuilts/vndk/v{version}.""" 18 19 import argparse 20 import glob 21 import logging 22 import os 23 import re 24 import shutil 25 import subprocess 26 import sys 27 import tempfile 28 import textwrap 29 30 import utils 31 32 from check_gpl_license import GPLChecker 33 from gen_buildfiles import GenBuildFile 34 35 ANDROID_BUILD_TOP = utils.get_android_build_top() 36 DIST_DIR = utils.get_dist_dir(utils.get_out_dir(ANDROID_BUILD_TOP)) 37 PREBUILTS_VNDK_DIR = utils.join_realpath(ANDROID_BUILD_TOP, 'prebuilts/vndk') 38 39 40 def logger(): 41 return logging.getLogger(__name__) 42 43 44 def check_call(cmd): 45 logger().debug('Running `{}`'.format(' '.join(cmd))) 46 subprocess.check_call(cmd) 47 48 49 def start_branch(build): 50 branch_name = 'update-' + (build or 'local') 51 logger().info('Creating branch {branch} in {dir}'.format( 52 branch=branch_name, dir=os.getcwd())) 53 check_call(['repo', 'start', branch_name, '.']) 54 55 56 def remove_old_snapshot(install_dir): 57 logger().info('Removing any old files in {}'.format(install_dir)) 58 for file in glob.glob('{}/*'.format(install_dir)): 59 try: 60 if os.path.isfile(file): 61 os.unlink(file) 62 elif os.path.isdir(file): 63 shutil.rmtree(file) 64 except Exception as error: 65 print error 66 sys.exit(1) 67 68 69 def install_snapshot(branch, build, install_dir, temp_artifact_dir): 70 """Installs VNDK snapshot build artifacts to prebuilts/vndk/v{version}. 71 72 1) Fetch build artifacts from Android Build server or from local DIST_DIR 73 2) Unzip build artifacts 74 75 Args: 76 branch: string or None, branch name of build artifacts 77 build: string or None, build number of build artifacts 78 install_dir: string, directory to install VNDK snapshot 79 temp_artifact_dir: string, temp directory to hold build artifacts fetched 80 from Android Build server. For 'local' option, is set to None. 81 """ 82 artifact_pattern = 'android-vndk-*.zip' 83 84 if branch and build: 85 artifact_dir = temp_artifact_dir 86 os.chdir(temp_artifact_dir) 87 logger().info('Fetching {pattern} from {branch} (bid: {build})'.format( 88 pattern=artifact_pattern, branch=branch, build=build)) 89 utils.fetch_artifact(branch, build, artifact_pattern) 90 91 manifest_pattern = 'manifest_{}.xml'.format(build) 92 logger().info('Fetching {file} from {branch} (bid: {build})'.format( 93 file=manifest_pattern, branch=branch, build=build)) 94 utils.fetch_artifact(branch, build, manifest_pattern, 95 utils.MANIFEST_FILE_NAME) 96 97 os.chdir(install_dir) 98 else: 99 logger().info('Fetching local VNDK snapshot from {}'.format(DIST_DIR)) 100 artifact_dir = DIST_DIR 101 102 artifacts = glob.glob(os.path.join(artifact_dir, artifact_pattern)) 103 artifact_cnt = len(artifacts) 104 if artifact_cnt < 4: 105 raise RuntimeError( 106 'Expected four android-vndk-*.zip files in {path}. Instead ' 107 'found {cnt}.'.format(path=artifact_dir, cnt=artifact_cnt)) 108 for artifact in artifacts: 109 logger().info('Unzipping VNDK snapshot: {}'.format(artifact)) 110 check_call(['unzip', '-q', artifact, '-d', install_dir]) 111 112 113 def gather_notice_files(install_dir): 114 """Gathers all NOTICE files to a common NOTICE_FILES directory.""" 115 116 common_notices_dir = utils.NOTICE_FILES_DIR_PATH 117 logger().info('Creating {} directory...'.format(common_notices_dir)) 118 os.makedirs(common_notices_dir) 119 for variant in utils.get_snapshot_variants(install_dir): 120 notices_dir_per_variant = os.path.join(variant, 121 utils.NOTICE_FILES_DIR_NAME) 122 if os.path.isdir(notices_dir_per_variant): 123 for notice_file in glob.glob( 124 '{}/*.txt'.format(notices_dir_per_variant)): 125 if not os.path.isfile( 126 os.path.join(common_notices_dir, 127 os.path.basename(notice_file))): 128 shutil.copy(notice_file, common_notices_dir) 129 shutil.rmtree(notices_dir_per_variant) 130 131 132 def revise_ld_config_txt_if_needed(vndk_version): 133 """For O-MR1, replaces unversioned VNDK directories with versioned ones. 134 135 Unversioned VNDK directories: /system/${LIB}/vndk[-sp] 136 Versioned VNDK directories: /system/${LIB}/vndk[-sp]-27 137 138 Args: 139 vndk_version: string, version of VNDK snapshot 140 """ 141 if vndk_version == '27': 142 re_pattern = '(system\/\${LIB}\/vndk(?:-sp)?)([:/]|$)' 143 VNDK_INSTALL_DIR_RE = re.compile(re_pattern, flags=re.MULTILINE) 144 ld_config_txt_paths = glob.glob( 145 os.path.join(utils.CONFIG_DIR_PATH_PATTERN, 'ld.config*')) 146 for ld_config_file in ld_config_txt_paths: 147 with open(ld_config_file, 'r') as file: 148 revised = VNDK_INSTALL_DIR_RE.sub(r'\1-27\2', file.read()) 149 with open(ld_config_file, 'w') as file: 150 file.write(revised) 151 152 153 def update_buildfiles(buildfile_generator): 154 logger().info('Generating Android.mk file...') 155 buildfile_generator.generate_android_mk() 156 157 logger().info('Generating Android.bp files...') 158 buildfile_generator.generate_android_bp() 159 160 161 def check_gpl_license(license_checker): 162 try: 163 license_checker.check_gpl_projects() 164 except ValueError as error: 165 print '***CANNOT INSTALL VNDK SNAPSHOT***', error 166 raise 167 168 169 def commit(branch, build, version): 170 logger().info('Making commit...') 171 check_call(['git', 'add', '.']) 172 message = textwrap.dedent("""\ 173 Update VNDK snapshot v{version} to build {build}. 174 175 Taken from branch {branch}.""").format( 176 version=version, branch=branch, build=build) 177 check_call(['git', 'commit', '-m', message]) 178 179 180 def get_args(): 181 parser = argparse.ArgumentParser() 182 parser.add_argument( 183 'vndk_version', type=int, 184 help='VNDK snapshot version to install, e.g. "27".') 185 parser.add_argument('-b', '--branch', help='Branch to pull build from.') 186 parser.add_argument('--build', help='Build number to pull.') 187 parser.add_argument( 188 '--local', action='store_true', 189 help=('Fetch local VNDK snapshot artifacts from DIST_DIR instead of ' 190 'Android Build server.')) 191 parser.add_argument( 192 '--use-current-branch', action='store_true', 193 help='Perform the update in the current branch. Do not repo start.') 194 parser.add_argument( 195 '-v', '--verbose', action='count', default=0, 196 help='Increase output verbosity, e.g. "-v", "-vv".') 197 return parser.parse_args() 198 199 200 def main(): 201 """Program entry point.""" 202 args = get_args() 203 204 if args.local: 205 if args.build or args.branch: 206 raise ValueError( 207 'When --local option is set, --branch or --build cannot be ' 208 'specified.') 209 elif not os.path.isdir(DIST_DIR): 210 raise RuntimeError( 211 'The --local option is set, but DIST_DIR={} does not exist.'. 212 format(DIST_DIR)) 213 else: 214 if not (args.build and args.branch): 215 raise ValueError( 216 'Please provide both --branch and --build or set --local ' 217 'option.') 218 219 vndk_version = str(args.vndk_version) 220 221 install_dir = os.path.join(PREBUILTS_VNDK_DIR, 'v{}'.format(vndk_version)) 222 if not os.path.isdir(install_dir): 223 raise RuntimeError( 224 'The directory for VNDK snapshot version {ver} does not exist.\n' 225 'Please request a new git project for prebuilts/vndk/v{ver} ' 226 'before installing new snapshot.'.format(ver=vndk_version)) 227 228 verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG) 229 verbosity = min(args.verbose, 2) 230 logging.basicConfig(level=verbose_map[verbosity]) 231 232 os.chdir(install_dir) 233 234 if not args.use_current_branch: 235 start_branch(args.build) 236 237 remove_old_snapshot(install_dir) 238 os.makedirs(utils.COMMON_DIR_PATH) 239 240 temp_artifact_dir = None 241 if not args.local: 242 temp_artifact_dir = tempfile.mkdtemp() 243 244 install_status = True 245 try: 246 install_snapshot(args.branch, args.build, install_dir, 247 temp_artifact_dir) 248 gather_notice_files(install_dir) 249 revise_ld_config_txt_if_needed(vndk_version) 250 251 buildfile_generator = GenBuildFile(install_dir, vndk_version) 252 update_buildfiles(buildfile_generator) 253 254 if not args.local: 255 license_checker = GPLChecker(install_dir, ANDROID_BUILD_TOP, 256 temp_artifact_dir) 257 check_gpl_license(license_checker) 258 except: 259 logger().info('FAILED TO INSTALL SNAPSHOT') 260 install_status = False 261 finally: 262 if temp_artifact_dir: 263 logger().info( 264 'Deleting temp_artifact_dir: {}'.format(temp_artifact_dir)) 265 shutil.rmtree(temp_artifact_dir) 266 267 if not args.local and install_status: 268 commit(args.branch, args.build, vndk_version) 269 270 271 if __name__ == '__main__': 272 main() 273