Home | History | Annotate | Download | only in ndk
      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