1#!/usr/bin/env python3 2# 3# Copyright (C) 2019-2020 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 18import argparse 19import collections 20import functools 21import glob 22import json 23import logging 24import os 25import pathlib 26import re 27import shlex 28import shutil 29import subprocess 30import sys 31import tempfile 32import urllib.request 33 34from concurrent import futures 35from pathlib import Path 36 37BASE_URL = "https://ci.android.com/builds/submitted/{build_id}/{target}/latest/raw" 38SUPPORTED_ARCHS = ["arm64"] 39VARIANTS = ["userdebug"] 40BOOT_PREBUILT_REL_DIR = "packages/modules/BootPrebuilt" 41ANDROID_BUILD_TOP = os.environ["ANDROID_BUILD_TOP"] 42 43logger = logging.getLogger(__name__) 44logging.basicConfig(level=logging.INFO) 45 46def parse_args(): 47 parser = argparse.ArgumentParser() 48 parser.add_argument( 49 "build_id", 50 type=int, 51 help="the build id to download the build for, e.g. 6148204") 52 parser.add_argument( 53 "--bug", 54 type=str, 55 default=None, 56 help="optional bug number for git commit.") 57 58 return parser.parse_args() 59 60 61def download_file(url, dest_filename): 62 logger.info("Downloading %s -> %s", url, dest_filename) 63 urllib.request.urlretrieve(url, dest_filename) 64 65 66def get_artifact_download_spec(build_id, device, variant, dest_dir): 67 target = "{}-{}".format(device, variant) 68 url_base = BASE_URL.format(build_id=build_id, target=target) 69 filename = "{}-img-{}.zip".format(device, build_id) 70 url = os.path.join(url_base, filename) 71 dest_filename = os.path.join(dest_dir, filename) 72 return url, dest_filename 73 74 75def update_prebuilt(build_id, boot_prebuilt, ver, arch, variant, bug): 76 device = "aosp_" + arch 77 arch_dir = os.path.join(boot_prebuilt, ver, arch) 78 variant_dir = os.path.join(arch_dir, variant) 79 boot_img_name = "boot-{}.img".format(ver) 80 stored_img_name = "boot-{}.img".format(variant) 81 try: 82 subprocess.check_call(["repo", "start", "boot-prebuilt-{}".format(build_id)], cwd=arch_dir) 83 84 os.makedirs(variant_dir) 85 url, dest_filename = get_artifact_download_spec(build_id, device, variant, variant_dir) 86 download_file(url, dest_filename) 87 args = ["unzip", "-d", variant_dir, dest_filename, boot_img_name] 88 logger.info("Calling: %s", " ".join(args)) 89 subprocess.check_call(args) 90 shutil.move(os.path.join(variant_dir, boot_img_name), os.path.join(arch_dir, stored_img_name)) 91 92 finally: 93 shutil.rmtree(variant_dir) 94 95 message = """Update prebuilts to {build_id}. 96 97Test: Treehugger 98Bug: {bug} 99""".format(build_id=build_id, bug=bug or "N/A") 100 101 logger.info("Creating commit for %s", arch_dir) 102 subprocess.check_call(["git", "add", "."], cwd=arch_dir) 103 subprocess.check_call(["git", "commit", "-m", message], cwd=arch_dir) 104 105 106def main(): 107 args = parse_args() 108 with futures.ThreadPoolExecutor(max_workers=10) as pool: 109 fs = [] 110 boot_prebuilt = os.path.join(ANDROID_BUILD_TOP, BOOT_PREBUILT_REL_DIR) 111 for ver in os.listdir(boot_prebuilt): 112 if not re.match(r'\d+[.]\d+', ver): 113 continue 114 for arch in os.listdir(os.path.join(boot_prebuilt, ver)): 115 if arch not in SUPPORTED_ARCHS: 116 continue 117 for variant in VARIANTS: 118 fs.append((ver, arch, pool.submit(update_prebuilt, args.build_id, boot_prebuilt, ver, 119 arch, variant, args.bug))) 120 121 futures.wait([f for ver, arch, f in fs]) 122 success_dirs = [] 123 logger.info("===============") 124 logger.info("Summary:") 125 for ver, arch, future in fs: 126 if future.exception(): 127 logger.error("%s/%s: %s", ver, arch, future.exception()) 128 else: 129 logger.info("%s/%s: Updated.", ver, arch) 130 success_dirs.append(os.path.join(BOOT_PREBUILT_REL_DIR, ver, arch)) 131 132 if success_dirs: 133 args = ["repo", "upload", "--verify", "--hashtag-branch", "--br", 134 "boot-prebuilt-{}".format(args.build_id)] + success_dirs 135 logger.info(" ".join(args)) 136 subprocess.check_call(args, cwd=ANDROID_BUILD_TOP) 137 138 139if __name__ == "__main__": 140 sys.exit(main()) 141