1*da0073e9SAndroid Build Coastguard Worker# Generates RegisterCodegenUnboxedKernels.cpp, UnboxingFunctions.h and UnboxingFunctions.cpp. 2*da0073e9SAndroid Build Coastguard Worker 3*da0073e9SAndroid Build Coastguard Workerfrom __future__ import annotations 4*da0073e9SAndroid Build Coastguard Worker 5*da0073e9SAndroid Build Coastguard Workerimport argparse 6*da0073e9SAndroid Build Coastguard Workerimport os 7*da0073e9SAndroid Build Coastguard Workerimport sys 8*da0073e9SAndroid Build Coastguard Workerfrom dataclasses import dataclass 9*da0073e9SAndroid Build Coastguard Workerfrom pathlib import Path 10*da0073e9SAndroid Build Coastguard Workerfrom typing import Literal, Sequence, TYPE_CHECKING 11*da0073e9SAndroid Build Coastguard Worker 12*da0073e9SAndroid Build Coastguard Workerimport yaml 13*da0073e9SAndroid Build Coastguard Worker 14*da0073e9SAndroid Build Coastguard Workerfrom torchgen.api import cpp, unboxing 15*da0073e9SAndroid Build Coastguard Workerfrom torchgen.api.translate import translate 16*da0073e9SAndroid Build Coastguard Workerfrom torchgen.api.types import CppSignatureGroup 17*da0073e9SAndroid Build Coastguard Workerfrom torchgen.api.unboxing import convert_arguments 18*da0073e9SAndroid Build Coastguard Workerfrom torchgen.context import method_with_native_function 19*da0073e9SAndroid Build Coastguard Workerfrom torchgen.gen import cpp_string, get_custom_build_selector, parse_native_yaml 20*da0073e9SAndroid Build Coastguard Workerfrom torchgen.model import Argument, NativeFunction, NativeFunctionsGroup, Variant 21*da0073e9SAndroid Build Coastguard Workerfrom torchgen.utils import FileManager, make_file_manager, mapMaybe, Target 22*da0073e9SAndroid Build Coastguard Worker 23*da0073e9SAndroid Build Coastguard Worker 24*da0073e9SAndroid Build Coastguard Workerif TYPE_CHECKING: 25*da0073e9SAndroid Build Coastguard Worker from torchgen.selective_build.selector import SelectiveBuilder 26*da0073e9SAndroid Build Coastguard Worker 27*da0073e9SAndroid Build Coastguard Worker 28*da0073e9SAndroid Build Coastguard Worker# Generates UnboxingFunctions.h & UnboxingFunctions.cpp. 29*da0073e9SAndroid Build Coastguard Worker@dataclass(frozen=True) 30*da0073e9SAndroid Build Coastguard Workerclass ComputeUnboxingFunctions: 31*da0073e9SAndroid Build Coastguard Worker target: Literal[Target.DECLARATION, Target.DEFINITION] 32*da0073e9SAndroid Build Coastguard Worker selector: SelectiveBuilder 33*da0073e9SAndroid Build Coastguard Worker 34*da0073e9SAndroid Build Coastguard Worker @method_with_native_function 35*da0073e9SAndroid Build Coastguard Worker def __call__(self, f: NativeFunction) -> str: 36*da0073e9SAndroid Build Coastguard Worker if not self.selector.is_root_operator(f"aten::{f.func.name}"): 37*da0073e9SAndroid Build Coastguard Worker return "" 38*da0073e9SAndroid Build Coastguard Worker 39*da0073e9SAndroid Build Coastguard Worker if self.target is Target.DECLARATION: 40*da0073e9SAndroid Build Coastguard Worker # Note [The ATen Codegen Unboxing API] 41*da0073e9SAndroid Build Coastguard Worker # Similar to the ATen Operators API, ATen Codegen Unboxing API lives in the at::unboxing namespace, and 42*da0073e9SAndroid Build Coastguard Worker # will be used by codegen unboxing wrappers (CodegenUnboxingWrappers.cpp). 43*da0073e9SAndroid Build Coastguard Worker # The Wrappers will be registered into torch::jit::OperatorRegistry using RegisterOperators API. 44*da0073e9SAndroid Build Coastguard Worker # 45*da0073e9SAndroid Build Coastguard Worker # Important characteristics about the Codegen Unboxing API: 46*da0073e9SAndroid Build Coastguard Worker # (1) It follows the OperatorRegistry API. 47*da0073e9SAndroid Build Coastguard Worker # This is kind of necessary to avoid overhead. 48*da0073e9SAndroid Build Coastguard Worker # For example: if it followed the C++ API, then all of the faithful C++ factory functions 49*da0073e9SAndroid Build Coastguard Worker # would need to wrap their arguments into TensorOptions only to unwrap them again. 50*da0073e9SAndroid Build Coastguard Worker # (2) Under the hood it calls C++ API. 51*da0073e9SAndroid Build Coastguard Worker return f""" 52*da0073e9SAndroid Build Coastguard Worker// aten::{f.func} 53*da0073e9SAndroid Build Coastguard WorkerTORCH_API void {f.func.name.unambiguous_name()}(Stack & stack); 54*da0073e9SAndroid Build Coastguard Worker""" 55*da0073e9SAndroid Build Coastguard Worker else: 56*da0073e9SAndroid Build Coastguard Worker sig_group = CppSignatureGroup.from_native_function( 57*da0073e9SAndroid Build Coastguard Worker f, method=(Variant.method in f.variants) 58*da0073e9SAndroid Build Coastguard Worker ) 59*da0073e9SAndroid Build Coastguard Worker sig = sig_group.most_faithful_signature() 60*da0073e9SAndroid Build Coastguard Worker # parse arguments into C++ code 61*da0073e9SAndroid Build Coastguard Worker binding_list, code_list = convert_arguments(f) 62*da0073e9SAndroid Build Coastguard Worker 63*da0073e9SAndroid Build Coastguard Worker # for each C++ argument, generate the conversion code 64*da0073e9SAndroid Build Coastguard Worker code_connector = "\n\t" 65*da0073e9SAndroid Build Coastguard Worker arg_connector = ", " 66*da0073e9SAndroid Build Coastguard Worker # function call and push back to stack 67*da0073e9SAndroid Build Coastguard Worker prefix = "self_base." if sig.method else "at::" 68*da0073e9SAndroid Build Coastguard Worker translated_args = translate( 69*da0073e9SAndroid Build Coastguard Worker binding_list, sig.arguments(), method=sig.method 70*da0073e9SAndroid Build Coastguard Worker ) 71*da0073e9SAndroid Build Coastguard Worker args_str = f"{arg_connector.join(e.expr for e in translated_args)}" 72*da0073e9SAndroid Build Coastguard Worker if len(f.func.returns) == 0: 73*da0073e9SAndroid Build Coastguard Worker ret_str = "" 74*da0073e9SAndroid Build Coastguard Worker push_str = "" 75*da0073e9SAndroid Build Coastguard Worker else: 76*da0073e9SAndroid Build Coastguard Worker ret_str = "auto result_ = " 77*da0073e9SAndroid Build Coastguard Worker push_str = """ 78*da0073e9SAndroid Build Coastguard Worker pack(stack, std::move(result_)); 79*da0073e9SAndroid Build Coastguard Worker """ 80*da0073e9SAndroid Build Coastguard Worker return f""" 81*da0073e9SAndroid Build Coastguard Worker// aten::{f.func} 82*da0073e9SAndroid Build Coastguard WorkerTORCH_API void {f.func.name.unambiguous_name()}(Stack & stack) {{ 83*da0073e9SAndroid Build Coastguard Worker {code_connector.join(code_list)} 84*da0073e9SAndroid Build Coastguard Worker 85*da0073e9SAndroid Build Coastguard Worker drop(stack, {len(binding_list)}); 86*da0073e9SAndroid Build Coastguard Worker 87*da0073e9SAndroid Build Coastguard Worker {ret_str}{prefix}{sig.name()}({args_str}); 88*da0073e9SAndroid Build Coastguard Worker {push_str} 89*da0073e9SAndroid Build Coastguard Worker}} 90*da0073e9SAndroid Build Coastguard Worker""" 91*da0073e9SAndroid Build Coastguard Worker 92*da0073e9SAndroid Build Coastguard Worker 93*da0073e9SAndroid Build Coastguard Worker# Generates RegisterCodegenUnboxedKernels.cpp. 94*da0073e9SAndroid Build Coastguard Worker@dataclass(frozen=True) 95*da0073e9SAndroid Build Coastguard Workerclass ComputeCodegenUnboxedKernels: 96*da0073e9SAndroid Build Coastguard Worker selector: SelectiveBuilder 97*da0073e9SAndroid Build Coastguard Worker 98*da0073e9SAndroid Build Coastguard Worker @method_with_native_function 99*da0073e9SAndroid Build Coastguard Worker def __call__(self, f: NativeFunction) -> str: 100*da0073e9SAndroid Build Coastguard Worker if not self.selector.is_root_operator(f"aten::{f.func.name}"): 101*da0073e9SAndroid Build Coastguard Worker return "" 102*da0073e9SAndroid Build Coastguard Worker # We unconditionally generate function wrappers, 103*da0073e9SAndroid Build Coastguard Worker sig_group = CppSignatureGroup.from_native_function(f, method=False) 104*da0073e9SAndroid Build Coastguard Worker 105*da0073e9SAndroid Build Coastguard Worker sig = sig_group.most_faithful_signature() 106*da0073e9SAndroid Build Coastguard Worker 107*da0073e9SAndroid Build Coastguard Worker # escape double quote in schema, get rid of extra double quotes 108*da0073e9SAndroid Build Coastguard Worker schema = cpp_string(str(sig.func))[1:-1] 109*da0073e9SAndroid Build Coastguard Worker 110*da0073e9SAndroid Build Coastguard Worker # arguments 111*da0073e9SAndroid Build Coastguard Worker args = sig.arguments() 112*da0073e9SAndroid Build Coastguard Worker connector = ",\n\t\t" 113*da0073e9SAndroid Build Coastguard Worker args_code = [] 114*da0073e9SAndroid Build Coastguard Worker for arg in args: 115*da0073e9SAndroid Build Coastguard Worker # Using method=False faithful C++ API, so we should not see SelfArgument/TensorOptionsArgument 116*da0073e9SAndroid Build Coastguard Worker assert isinstance(arg.argument, Argument) 117*da0073e9SAndroid Build Coastguard Worker if not arg.argument.default: 118*da0073e9SAndroid Build Coastguard Worker arg_cpp = "c10::IValue(::std::nullopt)" 119*da0073e9SAndroid Build Coastguard Worker else: 120*da0073e9SAndroid Build Coastguard Worker # The unboxing code uses the faithful C++ API to avoid the overhead 121*da0073e9SAndroid Build Coastguard Worker # from wrapping/unwrapping TensorOptios. 122*da0073e9SAndroid Build Coastguard Worker # However, we would look to include default args for schema parsing. 123*da0073e9SAndroid Build Coastguard Worker # Default args only show up in the nonfaithful C++ API, 124*da0073e9SAndroid Build Coastguard Worker arg_default = cpp.default_expr( 125*da0073e9SAndroid Build Coastguard Worker arg.argument.default, arg.argument.type, symint=False 126*da0073e9SAndroid Build Coastguard Worker ) 127*da0073e9SAndroid Build Coastguard Worker if arg_default.startswith("{"): 128*da0073e9SAndroid Build Coastguard Worker arg_cpp = f"c10::IntArrayRef({arg_default})" 129*da0073e9SAndroid Build Coastguard Worker else: 130*da0073e9SAndroid Build Coastguard Worker arg_cpp = f"c10::IValue({arg_default})" 131*da0073e9SAndroid Build Coastguard Worker args_code.append( 132*da0073e9SAndroid Build Coastguard Worker f"""c10::Argument("{arg.name}", nullptr, ::std::nullopt, {arg_cpp})""" 133*da0073e9SAndroid Build Coastguard Worker ) 134*da0073e9SAndroid Build Coastguard Worker 135*da0073e9SAndroid Build Coastguard Worker returns = f.func.returns 136*da0073e9SAndroid Build Coastguard Worker returns_code = [] 137*da0073e9SAndroid Build Coastguard Worker for ret in returns: 138*da0073e9SAndroid Build Coastguard Worker returns_code.append(f"""c10::Argument("{ret.name if ret.name else ""}")""") 139*da0073e9SAndroid Build Coastguard Worker return f""" 140*da0073e9SAndroid Build Coastguard Worker// aten::{schema} 141*da0073e9SAndroid Build Coastguard WorkerOperatorGenerator( 142*da0073e9SAndroid Build Coastguard Worker "aten::{f.func.name.name}", 143*da0073e9SAndroid Build Coastguard Worker "{f.func.name.overload_name}", 144*da0073e9SAndroid Build Coastguard Worker {{ 145*da0073e9SAndroid Build Coastguard Worker {connector.join(args_code)} 146*da0073e9SAndroid Build Coastguard Worker }}, 147*da0073e9SAndroid Build Coastguard Worker {{ 148*da0073e9SAndroid Build Coastguard Worker {connector.join(returns_code)} 149*da0073e9SAndroid Build Coastguard Worker }}, 150*da0073e9SAndroid Build Coastguard Worker [](Stack & stack) {{ 151*da0073e9SAndroid Build Coastguard Worker RECORD_FUNCTION("{sig.name()}", std::vector<c10::IValue>()); 152*da0073e9SAndroid Build Coastguard Worker at::unboxing::{unboxing.name(f)}(stack); 153*da0073e9SAndroid Build Coastguard Worker }}, 154*da0073e9SAndroid Build Coastguard Worker aliasAnalysisFromSchema() 155*da0073e9SAndroid Build Coastguard Worker), 156*da0073e9SAndroid Build Coastguard Worker""" 157*da0073e9SAndroid Build Coastguard Worker 158*da0073e9SAndroid Build Coastguard Worker 159*da0073e9SAndroid Build Coastguard Workerdef gen_unboxing( 160*da0073e9SAndroid Build Coastguard Worker *, 161*da0073e9SAndroid Build Coastguard Worker native_functions: Sequence[NativeFunction], 162*da0073e9SAndroid Build Coastguard Worker cpu_fm: FileManager, 163*da0073e9SAndroid Build Coastguard Worker selector: SelectiveBuilder, 164*da0073e9SAndroid Build Coastguard Worker) -> None: 165*da0073e9SAndroid Build Coastguard Worker def key_func(fn: NativeFunction | NativeFunctionsGroup) -> str: 166*da0073e9SAndroid Build Coastguard Worker return fn.root_name 167*da0073e9SAndroid Build Coastguard Worker 168*da0073e9SAndroid Build Coastguard Worker selected_op_num: int = len(selector.operators) 169*da0073e9SAndroid Build Coastguard Worker # a best practice threshold of operators to enable sharding 170*da0073e9SAndroid Build Coastguard Worker sharding_threshold: int = 100 171*da0073e9SAndroid Build Coastguard Worker cpu_fm.write_sharded( 172*da0073e9SAndroid Build Coastguard Worker "UnboxingFunctions.cpp", 173*da0073e9SAndroid Build Coastguard Worker native_functions, 174*da0073e9SAndroid Build Coastguard Worker key_fn=key_func, 175*da0073e9SAndroid Build Coastguard Worker env_callable=lambda fn: { 176*da0073e9SAndroid Build Coastguard Worker "definitions": [ComputeUnboxingFunctions(Target.DEFINITION, selector)(fn)] 177*da0073e9SAndroid Build Coastguard Worker }, 178*da0073e9SAndroid Build Coastguard Worker num_shards=1 if selected_op_num < sharding_threshold else 5, 179*da0073e9SAndroid Build Coastguard Worker sharded_keys={"definitions"}, 180*da0073e9SAndroid Build Coastguard Worker ) 181*da0073e9SAndroid Build Coastguard Worker cpu_fm.write( 182*da0073e9SAndroid Build Coastguard Worker "UnboxingFunctions.h", 183*da0073e9SAndroid Build Coastguard Worker lambda: { 184*da0073e9SAndroid Build Coastguard Worker "declarations": list( 185*da0073e9SAndroid Build Coastguard Worker mapMaybe( 186*da0073e9SAndroid Build Coastguard Worker ComputeUnboxingFunctions(Target.DECLARATION, selector), 187*da0073e9SAndroid Build Coastguard Worker native_functions, 188*da0073e9SAndroid Build Coastguard Worker ) 189*da0073e9SAndroid Build Coastguard Worker ), 190*da0073e9SAndroid Build Coastguard Worker }, 191*da0073e9SAndroid Build Coastguard Worker ) 192*da0073e9SAndroid Build Coastguard Worker cpu_fm.write_sharded( 193*da0073e9SAndroid Build Coastguard Worker "RegisterCodegenUnboxedKernels.cpp", 194*da0073e9SAndroid Build Coastguard Worker native_functions, 195*da0073e9SAndroid Build Coastguard Worker key_fn=key_func, 196*da0073e9SAndroid Build Coastguard Worker env_callable=lambda fn: { 197*da0073e9SAndroid Build Coastguard Worker "unboxed_ops": [ComputeCodegenUnboxedKernels(selector)(fn)] 198*da0073e9SAndroid Build Coastguard Worker }, 199*da0073e9SAndroid Build Coastguard Worker num_shards=1 if selected_op_num < sharding_threshold else 10, 200*da0073e9SAndroid Build Coastguard Worker sharded_keys={"unboxed_ops"}, 201*da0073e9SAndroid Build Coastguard Worker ) 202*da0073e9SAndroid Build Coastguard Worker 203*da0073e9SAndroid Build Coastguard Worker 204*da0073e9SAndroid Build Coastguard Workerdef main(args: list[str]) -> None: 205*da0073e9SAndroid Build Coastguard Worker parser = argparse.ArgumentParser(description="Generate unboxing source files") 206*da0073e9SAndroid Build Coastguard Worker parser.add_argument( 207*da0073e9SAndroid Build Coastguard Worker "-s", 208*da0073e9SAndroid Build Coastguard Worker "--source-path", 209*da0073e9SAndroid Build Coastguard Worker help="path to source directory for ATen", 210*da0073e9SAndroid Build Coastguard Worker default="aten/src/ATen", 211*da0073e9SAndroid Build Coastguard Worker ) 212*da0073e9SAndroid Build Coastguard Worker parser.add_argument( 213*da0073e9SAndroid Build Coastguard Worker "-d", 214*da0073e9SAndroid Build Coastguard Worker "--install-dir", 215*da0073e9SAndroid Build Coastguard Worker "--install_dir", 216*da0073e9SAndroid Build Coastguard Worker help="output directory", 217*da0073e9SAndroid Build Coastguard Worker default="build/aten/src/ATen", 218*da0073e9SAndroid Build Coastguard Worker ) 219*da0073e9SAndroid Build Coastguard Worker parser.add_argument( 220*da0073e9SAndroid Build Coastguard Worker "-o", 221*da0073e9SAndroid Build Coastguard Worker "--output-dependencies", 222*da0073e9SAndroid Build Coastguard Worker help="output a list of dependencies into the given file and exit", 223*da0073e9SAndroid Build Coastguard Worker ) 224*da0073e9SAndroid Build Coastguard Worker parser.add_argument( 225*da0073e9SAndroid Build Coastguard Worker "--dry-run", 226*da0073e9SAndroid Build Coastguard Worker action="store_true", 227*da0073e9SAndroid Build Coastguard Worker help="run without writing any files (still updates outputs)", 228*da0073e9SAndroid Build Coastguard Worker ) 229*da0073e9SAndroid Build Coastguard Worker parser.add_argument( 230*da0073e9SAndroid Build Coastguard Worker "--op-selection-yaml-path", 231*da0073e9SAndroid Build Coastguard Worker "--op_selection_yaml_path", 232*da0073e9SAndroid Build Coastguard Worker help="Provide a path to the operator selection (for custom build) YAML " 233*da0073e9SAndroid Build Coastguard Worker "that contains the information about the set of selected operators " 234*da0073e9SAndroid Build Coastguard Worker "and their categories (training, ...). Each operator is either a " 235*da0073e9SAndroid Build Coastguard Worker "full operator name with overload or just a bare operator name. " 236*da0073e9SAndroid Build Coastguard Worker "The operator names also contain the namespace prefix (e.g. aten::)", 237*da0073e9SAndroid Build Coastguard Worker ) 238*da0073e9SAndroid Build Coastguard Worker parser.add_argument( 239*da0073e9SAndroid Build Coastguard Worker "--op-registration-allowlist", 240*da0073e9SAndroid Build Coastguard Worker "--op_registration_allowlist", 241*da0073e9SAndroid Build Coastguard Worker nargs="*", 242*da0073e9SAndroid Build Coastguard Worker help="filter op registrations by the allowlist (if set); " 243*da0073e9SAndroid Build Coastguard Worker "each item is `namespace`::`operator name` without overload name; " 244*da0073e9SAndroid Build Coastguard Worker "e.g.: aten::empty aten::conv2d ...", 245*da0073e9SAndroid Build Coastguard Worker ) 246*da0073e9SAndroid Build Coastguard Worker parser.add_argument( 247*da0073e9SAndroid Build Coastguard Worker "--TEST-ONLY-op-registration-allowlist-yaml-path", 248*da0073e9SAndroid Build Coastguard Worker "--TEST_ONLY_op_registration_allowlist_yaml_path", 249*da0073e9SAndroid Build Coastguard Worker help="Provide a path to the operator selection (for custom build) YAML " 250*da0073e9SAndroid Build Coastguard Worker "which contains a list of operators. It is to serve testing purpose and " 251*da0073e9SAndroid Build Coastguard Worker "each item is `namespace`::`operator name` without overload name; " 252*da0073e9SAndroid Build Coastguard Worker "e.g.: aten::empty aten::conv2d ...", 253*da0073e9SAndroid Build Coastguard Worker ) 254*da0073e9SAndroid Build Coastguard Worker 255*da0073e9SAndroid Build Coastguard Worker options = parser.parse_args(args) 256*da0073e9SAndroid Build Coastguard Worker if options.op_registration_allowlist: 257*da0073e9SAndroid Build Coastguard Worker op_registration_allowlist = options.op_registration_allowlist 258*da0073e9SAndroid Build Coastguard Worker elif options.TEST_ONLY_op_registration_allowlist_yaml_path: 259*da0073e9SAndroid Build Coastguard Worker with open(options.TEST_ONLY_op_registration_allowlist_yaml_path) as f: 260*da0073e9SAndroid Build Coastguard Worker op_registration_allowlist = yaml.safe_load(f) 261*da0073e9SAndroid Build Coastguard Worker else: 262*da0073e9SAndroid Build Coastguard Worker op_registration_allowlist = None 263*da0073e9SAndroid Build Coastguard Worker 264*da0073e9SAndroid Build Coastguard Worker selector = get_custom_build_selector( 265*da0073e9SAndroid Build Coastguard Worker op_registration_allowlist, 266*da0073e9SAndroid Build Coastguard Worker options.op_selection_yaml_path, 267*da0073e9SAndroid Build Coastguard Worker ) 268*da0073e9SAndroid Build Coastguard Worker 269*da0073e9SAndroid Build Coastguard Worker native_yaml_path = os.path.join(options.source_path, "native/native_functions.yaml") 270*da0073e9SAndroid Build Coastguard Worker tags_yaml_path = os.path.join(options.source_path, "native/tags.yaml") 271*da0073e9SAndroid Build Coastguard Worker parsed_yaml = parse_native_yaml(native_yaml_path, tags_yaml_path) 272*da0073e9SAndroid Build Coastguard Worker native_functions, backend_indices = ( 273*da0073e9SAndroid Build Coastguard Worker parsed_yaml.native_functions, 274*da0073e9SAndroid Build Coastguard Worker parsed_yaml.backend_indices, 275*da0073e9SAndroid Build Coastguard Worker ) 276*da0073e9SAndroid Build Coastguard Worker 277*da0073e9SAndroid Build Coastguard Worker cpu_fm = make_file_manager(options=options) 278*da0073e9SAndroid Build Coastguard Worker gen_unboxing(native_functions=native_functions, cpu_fm=cpu_fm, selector=selector) 279*da0073e9SAndroid Build Coastguard Worker 280*da0073e9SAndroid Build Coastguard Worker if options.output_dependencies: 281*da0073e9SAndroid Build Coastguard Worker depfile_path = Path(options.output_dependencies).resolve() 282*da0073e9SAndroid Build Coastguard Worker depfile_name = depfile_path.name 283*da0073e9SAndroid Build Coastguard Worker depfile_stem = depfile_path.stem 284*da0073e9SAndroid Build Coastguard Worker 285*da0073e9SAndroid Build Coastguard Worker path = depfile_path.parent / depfile_name 286*da0073e9SAndroid Build Coastguard Worker cpu_fm.write_outputs(depfile_stem, str(path)) 287*da0073e9SAndroid Build Coastguard Worker 288*da0073e9SAndroid Build Coastguard Worker 289*da0073e9SAndroid Build Coastguard Workerif __name__ == "__main__": 290*da0073e9SAndroid Build Coastguard Worker main(sys.argv[1:]) 291