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