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