1#!/usr/bin/python 2 3import os 4import shlex 5import sys 6 7BISECT_STAGE = os.environ.get('BISECT_STAGE') 8 9# We do not need bisect functionality with Goma and clang. 10# Goma server does not have bisect_driver, so we only import 11# bisect_driver when needed. See http://b/34862041 12# We should be careful when doing imports because of Goma. 13if BISECT_STAGE: 14 import bisect_driver 15 16DEFAULT_BISECT_DIR = os.path.expanduser('~/ANDROID_BISECT') 17BISECT_DIR = os.environ.get('BISECT_DIR') or DEFAULT_BISECT_DIR 18 19 20def ProcessArgFile(arg_file): 21 args = [] 22 # Read in entire file at once and parse as if in shell 23 with open(arg_file, 'rb') as f: 24 args.extend(shlex.split(f.read())) 25 return args 26 27 28class CompilerWrapper(): 29 def __init__(self, argv): 30 self.argv0_current = argv[0] 31 self.args = argv[1:] 32 self.execargs = [] 33 self.real_compiler = None 34 self.argv0 = None 35 self.append_flags = [] 36 self.prepend_flags = [] 37 self.custom_flags = { 38 '--gomacc-path': None 39 } 40 41 def set_real_compiler(self): 42 """Find the real compiler with the absolute path.""" 43 compiler_path = os.path.dirname(self.argv0_current) 44 if os.path.islink(__file__): 45 compiler = os.path.basename(os.readlink(__file__)) 46 else: 47 compiler = os.path.basename(os.path.abspath(__file__)) 48 self.real_compiler = os.path.join( 49 compiler_path, 50 compiler + '.real') 51 self.argv0 = self.real_compiler 52 53 def process_gomacc_command(self): 54 """Return the gomacc command if '--gomacc-path' is set.""" 55 gomacc = self.custom_flags['--gomacc-path'] 56 if gomacc and os.path.isfile(gomacc): 57 self.argv0 = gomacc 58 self.execargs += [gomacc] 59 60 def parse_custom_flags(self): 61 i = 0 62 args = [] 63 while i < len(self.args): 64 if self.args[i] in self.custom_flags: 65 if i >= len(self.args) - 1: 66 sys.exit('The value of {} is not set.'.format(self.args[i])) 67 self.custom_flags[self.args[i]] = self.args[i + 1] 68 i = i + 2 69 else: 70 args.append(self.args[i]) 71 i = i + 1 72 self.args = args 73 74 def add_flags(self): 75 self.args = self.prepend_flags + self.args + self.append_flags 76 77 def prepare_compiler_args(self): 78 self.set_real_compiler() 79 self.parse_custom_flags() 80 self.process_gomacc_command() 81 self.add_flags() 82 self.execargs += [self.real_compiler] + self.args 83 84 def invoke_compiler(self): 85 self.prepare_compiler_args() 86 os.execv(self.argv0, self.execargs) 87 88 def bisect(self): 89 self.prepare_compiler_args() 90 # Handle @file argument syntax with compiler 91 idx = 0 92 # The length of self.execargs can be changed during the @file argument 93 # expansion, so we need to use while loop instead of for loop. 94 while idx < len(self.execargs): 95 if self.execargs[idx][0] == '@': 96 args_in_file = ProcessArgFile(self.execargs[idx][1:]) 97 self.execargs = self.execargs[0:idx] + args_in_file + self.execargs[idx + 1:] 98 # Skip update of idx, since we want to recursively expand response files. 99 else: 100 idx = idx + 1 101 bisect_driver.bisect_driver(BISECT_STAGE, BISECT_DIR, self.execargs) 102 103 104def main(argv): 105 cw = CompilerWrapper(argv) 106 if BISECT_STAGE and BISECT_STAGE in bisect_driver.VALID_MODES and '-o' in argv: 107 cw.bisect() 108 else: 109 cw.invoke_compiler() 110 111 112if __name__ == '__main__': 113 main(sys.argv) 114