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 import argparse 18 import glob 19 import logging 20 import os 21 import shutil 22 import subprocess 23 import textwrap 24 25 26 THIS_DIR = os.path.realpath(os.path.dirname(__file__)) 27 28 29 def logger(): 30 return logging.getLogger(__name__) 31 32 33 def check_call(cmd): 34 logger().debug('Running `%s`', ' '.join(cmd)) 35 subprocess.check_call(cmd) 36 37 38 def fetch_artifact(branch, build, pattern): 39 fetch_artifact_path = '/google/data/ro/projects/android/fetch_artifact' 40 cmd = [fetch_artifact_path, '--branch', branch, '--target=linux', 41 '--bid', build, pattern] 42 check_call(cmd) 43 44 45 def api_str(api_level): 46 return 'android-{}'.format(api_level) 47 48 49 def start_branch(build): 50 branch_name = 'update-' + (build or 'latest') 51 logger().info('Creating branch %s', branch_name) 52 check_call(['repo', 'start', branch_name, '.']) 53 54 55 def remove_old_release(install_dir): 56 if os.path.exists(os.path.join(install_dir, '.git')): 57 logger().info('Removing old install directory "%s"', install_dir) 58 check_call(['git', 'rm', '-rf', install_dir]) 59 60 # Need to check again because git won't remove directories if they have 61 # non-git files in them. 62 if os.path.exists(install_dir): 63 shutil.rmtree(install_dir) 64 65 66 def install_new_release(branch, build, install_dir): 67 os.makedirs(install_dir) 68 69 artifact_pattern = 'android-ndk-*.tar.bz2' 70 logger().info('Fetching %s from %s (artifacts matching %s)', build, branch, 71 artifact_pattern) 72 fetch_artifact(branch, build, artifact_pattern) 73 artifacts = glob.glob('android-ndk-*.tar.bz2') 74 try: 75 assert len(artifacts) == 1 76 artifact = artifacts[0] 77 78 logger().info('Extracting release') 79 cmd = ['tar', 'xf', artifact, '-C', install_dir, '--wildcards', 80 '--strip-components=1', '*/platforms', '*/sources', 81 '*/source.properties'] 82 check_call(cmd) 83 finally: 84 for artifact in artifacts: 85 os.unlink(artifact) 86 87 88 def symlink_gaps(first, last): 89 for api in xrange(first, last + 1): 90 if os.path.exists(api_str(api)): 91 continue 92 93 # Not all API levels have a platform directory. Make a symlink to the 94 # previous API level. For example, symlink android-10 to android-9. 95 assert api != 9 96 os.symlink(api_str(api - 1), api_str(api)) 97 98 99 def make_symlinks(install_dir): 100 old_dir = os.getcwd() 101 os.chdir(os.path.join(THIS_DIR, install_dir, 'platforms')) 102 103 first_api = 9 104 first_lp64_api = 21 105 latest_api = 23 106 107 for api in xrange(first_api, first_lp64_api): 108 if not os.path.exists(api_str(api)): 109 continue 110 111 for arch in ('arch-arm64', 'arch-mips64', 'arch-x86_64'): 112 src = os.path.join('..', api_str(first_lp64_api), arch) 113 dst = os.path.join(api_str(api), arch) 114 if os.path.islink(dst): 115 os.unlink(dst) 116 os.symlink(src, dst) 117 118 symlink_gaps(first_api, latest_api) 119 os.chdir(old_dir) 120 121 122 def commit(branch, build, install_dir): 123 logger().info('Making commit') 124 check_call(['git', 'add', install_dir]) 125 message = textwrap.dedent("""\ 126 Update NDK prebuilts to build {build}. 127 128 Taken from branch {branch}.""").format(branch=branch, build=build) 129 check_call(['git', 'commit', '-m', message]) 130 131 132 def get_args(): 133 parser = argparse.ArgumentParser() 134 parser.add_argument( 135 '-b', '--branch', default='master-ndk', 136 help='Branch to pull build from.') 137 parser.add_argument( 138 'major_release', help='Major release being installed, e.g. "r11".') 139 parser.add_argument('--build', required=True, help='Build number to pull.') 140 parser.add_argument( 141 '--use-current-branch', action='store_true', 142 help='Perform the update in the current branch. Do not repo start.') 143 parser.add_argument( 144 '-v', '--verbose', action='count', help='Increase output verbosity.') 145 return parser.parse_args() 146 147 148 def main(): 149 os.chdir(THIS_DIR) 150 151 args = get_args() 152 verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG) 153 verbosity = args.verbose 154 if verbosity > 2: 155 verbosity = 2 156 logging.basicConfig(level=verbose_map[verbosity]) 157 158 install_dir = os.path.realpath(args.major_release) 159 160 if not args.use_current_branch: 161 start_branch(args.build) 162 remove_old_release(install_dir) 163 install_new_release(args.branch, args.build, install_dir) 164 make_symlinks(install_dir) 165 commit(args.branch, args.build, install_dir) 166 167 168 if __name__ == '__main__': 169 main() 170