1#!/usr/bin/env python3 2# 3# Copyright 2024 - 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 18"""Update Tool.""" 19 20import argparse 21 22from core.errors import WorkflowError 23from core.task_runner import Task 24from core.task_runner import TaskRunner 25from tools.update_aliases import get_aliases 26from tools.update_utils import combine_build_commands 27from tools.update_utils import combine_update_commands 28 29 30class Update: 31 """Updates a device.""" 32 33 def __init__(self, args): 34 self.args = args 35 36 @classmethod 37 def add_parser(cls, subparsers): 38 """Parse command line update arguments.""" 39 40 aliases = get_aliases() 41 epilog = 'Aliases:\n' 42 for alias in get_aliases().keys(): 43 name = alias 44 build_commands = (';').join(aliases[name].build()) 45 update_commands = (';').join(aliases[name].update()) 46 epilog += f' {name}:\n\t{build_commands}\n\t{update_commands}\n' 47 48 parser = subparsers.add_parser( 49 'update', epilog=epilog, formatter_class=argparse.RawTextHelpFormatter 50 ) 51 52 parser.add_argument('alias', nargs='*', default=[], type=str) 53 parser.add_argument( 54 '--build-only', 55 action='store_true', 56 help='only build the specified targets, do not update the device.', 57 ) 58 parser.add_argument( 59 '--update-only', 60 action='store_true', 61 help=( 62 'only update the device with prebuilt targets, do not build' 63 ' targets.' 64 ), 65 ) 66 parser.add_argument( 67 '--list-aliases', 68 action='store_true', 69 help='list aliases; used for autocomplete', 70 ) 71 72 def main(self): 73 """Main entrypoint for Update.""" 74 75 if self.args.list_aliases: 76 print(' '.join(get_aliases().keys())) 77 return 78 79 tasks = self.gather_tasks() 80 self.run_tasks(tasks) 81 82 def gather_tasks(self): 83 """Gathers tasks to run based on alias.""" 84 tasks = [] 85 build_tasks = [] 86 update_tasks = [] 87 88 requested_aliases = self.args.alias 89 aliases = get_aliases() 90 for a in requested_aliases: 91 if a not in aliases: 92 raise WorkflowError(f'unknown alias: {a}') 93 config = aliases[a] 94 build_tasks += config.build() 95 update_tasks += config.update() 96 97 # combine build tasks 98 build_tasks = combine_build_commands(build_tasks) 99 # combine update tasks 100 update_tasks = combine_update_commands(update_tasks) 101 102 if self.args.build_only: 103 tasks = build_tasks 104 elif self.args.update_only: 105 tasks = update_tasks 106 else: 107 tasks = build_tasks + update_tasks 108 109 if not tasks: 110 # If no tasks run adevice update with a fall back to a full flash. 111 tasks = [ 112 'm sync', 113 Task( 114 cmd='adevice update', 115 fall_back_tasks=[ 116 'm droid', 117 'flashall', 118 ], 119 ), 120 ] 121 return tasks 122 123 def run_tasks(self, tasks): 124 """Runs tasks.""" 125 task_runner = TaskRunner() 126 task_runner.quiet = False 127 for task in tasks: 128 if isinstance(task, str): 129 task_runner.add_shell_command_task(task) 130 elif isinstance(task, Task): 131 task_runner.add_shell_command_task(task.cmd, task.fall_back_tasks) 132 else: 133 task_runner.add_task(task) 134 task_runner.start() 135