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 19import argparse 20import glob 21import logging 22import os 23import re 24import shutil 25import subprocess 26import sys 27import tempfile 28import textwrap 29 30import utils 31 32from check_gpl_license import GPLChecker 33from gen_buildfiles import GenBuildFile 34 35ANDROID_BUILD_TOP = utils.get_android_build_top() 36DIST_DIR = utils.get_dist_dir(utils.get_out_dir(ANDROID_BUILD_TOP)) 37PREBUILTS_VNDK_DIR = utils.join_realpath(ANDROID_BUILD_TOP, 'prebuilts/vndk') 38 39 40def logger(): 41 return logging.getLogger(__name__) 42 43 44def check_call(cmd): 45 logger().debug('Running `{}`'.format(' '.join(cmd))) 46 subprocess.check_call(cmd) 47 48 49def 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 56def 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 69def 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 113def 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 132def 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 153def 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 161def 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 169def 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 180def 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 200def 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 271if __name__ == '__main__': 272 main() 273