1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6777b538SAndroid Build Coastguard Worker# Copyright 2020 The Chromium Authors 3*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 5*6777b538SAndroid Build Coastguard Worker"""Checks that compiling targets in BUILD.gn file fails.""" 6*6777b538SAndroid Build Coastguard Worker 7*6777b538SAndroid Build Coastguard Workerimport argparse 8*6777b538SAndroid Build Coastguard Workerimport json 9*6777b538SAndroid Build Coastguard Workerimport os 10*6777b538SAndroid Build Coastguard Workerimport subprocess 11*6777b538SAndroid Build Coastguard Workerimport re 12*6777b538SAndroid Build Coastguard Workerimport sys 13*6777b538SAndroid Build Coastguard Workerfrom util import build_utils 14*6777b538SAndroid Build Coastguard Worker 15*6777b538SAndroid Build Coastguard Worker_CHROMIUM_SRC = os.path.normpath(os.path.join(__file__, '..', '..', '..', '..')) 16*6777b538SAndroid Build Coastguard Worker_NINJA_PATH = os.path.join(_CHROMIUM_SRC, 'third_party', 'ninja', 'ninja') 17*6777b538SAndroid Build Coastguard Worker 18*6777b538SAndroid Build Coastguard Worker# Relative to _CHROMIUM_SRC 19*6777b538SAndroid Build Coastguard Worker_GN_SRC_REL_PATH = os.path.join('buildtools', 'linux64', 'gn') 20*6777b538SAndroid Build Coastguard Worker 21*6777b538SAndroid Build Coastguard Worker# Regex for determining whether compile failed because 'gn gen' needs to be run. 22*6777b538SAndroid Build Coastguard Worker_GN_GEN_REGEX = re.compile(r'ninja: (error|fatal):') 23*6777b538SAndroid Build Coastguard Worker 24*6777b538SAndroid Build Coastguard Worker 25*6777b538SAndroid Build Coastguard Workerdef _raise_command_exception(args, returncode, output): 26*6777b538SAndroid Build Coastguard Worker """Raises an exception whose message describes a command failure. 27*6777b538SAndroid Build Coastguard Worker 28*6777b538SAndroid Build Coastguard Worker Args: 29*6777b538SAndroid Build Coastguard Worker args: shell command-line (as passed to subprocess.Popen()) 30*6777b538SAndroid Build Coastguard Worker returncode: status code. 31*6777b538SAndroid Build Coastguard Worker output: command output. 32*6777b538SAndroid Build Coastguard Worker Raises: 33*6777b538SAndroid Build Coastguard Worker a new Exception. 34*6777b538SAndroid Build Coastguard Worker """ 35*6777b538SAndroid Build Coastguard Worker message = 'Command failed with status {}: {}\n' \ 36*6777b538SAndroid Build Coastguard Worker 'Output:-----------------------------------------\n{}\n' \ 37*6777b538SAndroid Build Coastguard Worker '------------------------------------------------\n'.format( 38*6777b538SAndroid Build Coastguard Worker returncode, args, output) 39*6777b538SAndroid Build Coastguard Worker raise Exception(message) 40*6777b538SAndroid Build Coastguard Worker 41*6777b538SAndroid Build Coastguard Worker 42*6777b538SAndroid Build Coastguard Workerdef _run_command(args, cwd=None): 43*6777b538SAndroid Build Coastguard Worker """Runs shell command. Raises exception if command fails.""" 44*6777b538SAndroid Build Coastguard Worker p = subprocess.Popen(args, 45*6777b538SAndroid Build Coastguard Worker stdout=subprocess.PIPE, 46*6777b538SAndroid Build Coastguard Worker stderr=subprocess.STDOUT, 47*6777b538SAndroid Build Coastguard Worker cwd=cwd) 48*6777b538SAndroid Build Coastguard Worker pout, _ = p.communicate() 49*6777b538SAndroid Build Coastguard Worker if p.returncode != 0: 50*6777b538SAndroid Build Coastguard Worker _raise_command_exception(args, p.returncode, pout) 51*6777b538SAndroid Build Coastguard Worker 52*6777b538SAndroid Build Coastguard Worker 53*6777b538SAndroid Build Coastguard Workerdef _run_command_get_failure_output(args): 54*6777b538SAndroid Build Coastguard Worker """Runs shell command. 55*6777b538SAndroid Build Coastguard Worker 56*6777b538SAndroid Build Coastguard Worker Returns: 57*6777b538SAndroid Build Coastguard Worker Command output if command fails, None if command succeeds. 58*6777b538SAndroid Build Coastguard Worker """ 59*6777b538SAndroid Build Coastguard Worker p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 60*6777b538SAndroid Build Coastguard Worker pout, _ = p.communicate() 61*6777b538SAndroid Build Coastguard Worker 62*6777b538SAndroid Build Coastguard Worker if p.returncode == 0: 63*6777b538SAndroid Build Coastguard Worker return None 64*6777b538SAndroid Build Coastguard Worker 65*6777b538SAndroid Build Coastguard Worker # For Python3 only: 66*6777b538SAndroid Build Coastguard Worker if isinstance(pout, bytes) and sys.version_info >= (3, ): 67*6777b538SAndroid Build Coastguard Worker pout = pout.decode('utf-8') 68*6777b538SAndroid Build Coastguard Worker return '' if pout is None else pout 69*6777b538SAndroid Build Coastguard Worker 70*6777b538SAndroid Build Coastguard Worker 71*6777b538SAndroid Build Coastguard Workerdef _copy_and_append_gn_args(src_args_path, dest_args_path, extra_args): 72*6777b538SAndroid Build Coastguard Worker """Copies args.gn. 73*6777b538SAndroid Build Coastguard Worker 74*6777b538SAndroid Build Coastguard Worker Args: 75*6777b538SAndroid Build Coastguard Worker src_args_path: args.gn file to copy. 76*6777b538SAndroid Build Coastguard Worker dest_args_path: Copy file destination. 77*6777b538SAndroid Build Coastguard Worker extra_args: Text to append to args.gn after copy. 78*6777b538SAndroid Build Coastguard Worker """ 79*6777b538SAndroid Build Coastguard Worker with open(src_args_path) as f_in, open(dest_args_path, 'w') as f_out: 80*6777b538SAndroid Build Coastguard Worker f_out.write(f_in.read()) 81*6777b538SAndroid Build Coastguard Worker f_out.write('\n') 82*6777b538SAndroid Build Coastguard Worker f_out.write('\n'.join(extra_args)) 83*6777b538SAndroid Build Coastguard Worker 84*6777b538SAndroid Build Coastguard Worker 85*6777b538SAndroid Build Coastguard Workerdef _find_regex_in_test_failure_output(test_output, regex): 86*6777b538SAndroid Build Coastguard Worker """Searches for regex in test output. 87*6777b538SAndroid Build Coastguard Worker 88*6777b538SAndroid Build Coastguard Worker Args: 89*6777b538SAndroid Build Coastguard Worker test_output: test output. 90*6777b538SAndroid Build Coastguard Worker regex: regular expression to search for. 91*6777b538SAndroid Build Coastguard Worker Returns: 92*6777b538SAndroid Build Coastguard Worker Whether the regular expression was found in the part of the test output 93*6777b538SAndroid Build Coastguard Worker after the 'FAILED' message. 94*6777b538SAndroid Build Coastguard Worker """ 95*6777b538SAndroid Build Coastguard Worker if test_output is None: 96*6777b538SAndroid Build Coastguard Worker return False 97*6777b538SAndroid Build Coastguard Worker 98*6777b538SAndroid Build Coastguard Worker failed_index = test_output.find('FAILED') 99*6777b538SAndroid Build Coastguard Worker if failed_index < 0: 100*6777b538SAndroid Build Coastguard Worker return False 101*6777b538SAndroid Build Coastguard Worker 102*6777b538SAndroid Build Coastguard Worker failure_message = test_output[failed_index:] 103*6777b538SAndroid Build Coastguard Worker if regex.find('\n') >= 0: 104*6777b538SAndroid Build Coastguard Worker return re.search(regex, failure_message) 105*6777b538SAndroid Build Coastguard Worker return _search_regex_in_list(failure_message.split('\n'), regex) 106*6777b538SAndroid Build Coastguard Worker 107*6777b538SAndroid Build Coastguard Worker 108*6777b538SAndroid Build Coastguard Workerdef _search_regex_in_list(value, regex): 109*6777b538SAndroid Build Coastguard Worker for line in value: 110*6777b538SAndroid Build Coastguard Worker if re.search(regex, line): 111*6777b538SAndroid Build Coastguard Worker return True 112*6777b538SAndroid Build Coastguard Worker return False 113*6777b538SAndroid Build Coastguard Worker 114*6777b538SAndroid Build Coastguard Worker 115*6777b538SAndroid Build Coastguard Workerdef _do_build_get_failure_output(gn_path, gn_cmd, options): 116*6777b538SAndroid Build Coastguard Worker # Extract directory from test target. As all of the test targets are declared 117*6777b538SAndroid Build Coastguard Worker # in the same BUILD.gn file, it does not matter which test target is used. 118*6777b538SAndroid Build Coastguard Worker target_dir = gn_path.rsplit(':', 1)[0] 119*6777b538SAndroid Build Coastguard Worker 120*6777b538SAndroid Build Coastguard Worker if gn_cmd is not None: 121*6777b538SAndroid Build Coastguard Worker gn_args = [ 122*6777b538SAndroid Build Coastguard Worker _GN_SRC_REL_PATH, '--root-target=' + target_dir, gn_cmd, 123*6777b538SAndroid Build Coastguard Worker os.path.relpath(options.out_dir, _CHROMIUM_SRC) 124*6777b538SAndroid Build Coastguard Worker ] 125*6777b538SAndroid Build Coastguard Worker _run_command(gn_args, cwd=_CHROMIUM_SRC) 126*6777b538SAndroid Build Coastguard Worker 127*6777b538SAndroid Build Coastguard Worker ninja_args = [_NINJA_PATH, '-C', options.out_dir, gn_path] 128*6777b538SAndroid Build Coastguard Worker return _run_command_get_failure_output(ninja_args) 129*6777b538SAndroid Build Coastguard Worker 130*6777b538SAndroid Build Coastguard Worker 131*6777b538SAndroid Build Coastguard Workerdef main(): 132*6777b538SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 133*6777b538SAndroid Build Coastguard Worker parser.add_argument('--gn-args-path', 134*6777b538SAndroid Build Coastguard Worker required=True, 135*6777b538SAndroid Build Coastguard Worker help='Path to args.gn file.') 136*6777b538SAndroid Build Coastguard Worker parser.add_argument('--test-configs-path', 137*6777b538SAndroid Build Coastguard Worker required=True, 138*6777b538SAndroid Build Coastguard Worker help='Path to file with test configurations') 139*6777b538SAndroid Build Coastguard Worker parser.add_argument('--out-dir', 140*6777b538SAndroid Build Coastguard Worker required=True, 141*6777b538SAndroid Build Coastguard Worker help='Path to output directory to use for compilation.') 142*6777b538SAndroid Build Coastguard Worker parser.add_argument('--stamp', help='Path to touch.') 143*6777b538SAndroid Build Coastguard Worker options = parser.parse_args() 144*6777b538SAndroid Build Coastguard Worker 145*6777b538SAndroid Build Coastguard Worker with open(options.test_configs_path) as f: 146*6777b538SAndroid Build Coastguard Worker # Escape '\' in '\.' now. This avoids having to do the escaping in the test 147*6777b538SAndroid Build Coastguard Worker # specification. 148*6777b538SAndroid Build Coastguard Worker config_text = f.read().replace(r'\.', r'\\.') 149*6777b538SAndroid Build Coastguard Worker test_configs = json.loads(config_text) 150*6777b538SAndroid Build Coastguard Worker 151*6777b538SAndroid Build Coastguard Worker if not os.path.exists(options.out_dir): 152*6777b538SAndroid Build Coastguard Worker os.makedirs(options.out_dir) 153*6777b538SAndroid Build Coastguard Worker 154*6777b538SAndroid Build Coastguard Worker out_gn_args_path = os.path.join(options.out_dir, 'args.gn') 155*6777b538SAndroid Build Coastguard Worker extra_gn_args = [ 156*6777b538SAndroid Build Coastguard Worker 'enable_android_nocompile_tests = true', 157*6777b538SAndroid Build Coastguard Worker 'treat_warnings_as_errors = true', 158*6777b538SAndroid Build Coastguard Worker # RBE does not work with non-standard output directories. 159*6777b538SAndroid Build Coastguard Worker 'use_remoteexec = false', 160*6777b538SAndroid Build Coastguard Worker ] 161*6777b538SAndroid Build Coastguard Worker _copy_and_append_gn_args(options.gn_args_path, out_gn_args_path, 162*6777b538SAndroid Build Coastguard Worker extra_gn_args) 163*6777b538SAndroid Build Coastguard Worker 164*6777b538SAndroid Build Coastguard Worker ran_gn_gen = False 165*6777b538SAndroid Build Coastguard Worker did_clean_build = False 166*6777b538SAndroid Build Coastguard Worker error_messages = [] 167*6777b538SAndroid Build Coastguard Worker for config in test_configs: 168*6777b538SAndroid Build Coastguard Worker # Strip leading '//' 169*6777b538SAndroid Build Coastguard Worker gn_path = config['target'][2:] 170*6777b538SAndroid Build Coastguard Worker expect_regex = config['expect_regex'] 171*6777b538SAndroid Build Coastguard Worker 172*6777b538SAndroid Build Coastguard Worker test_output = _do_build_get_failure_output(gn_path, None, options) 173*6777b538SAndroid Build Coastguard Worker 174*6777b538SAndroid Build Coastguard Worker # 'gn gen' takes > 1s to run. Only run 'gn gen' if it is needed for compile. 175*6777b538SAndroid Build Coastguard Worker if (test_output 176*6777b538SAndroid Build Coastguard Worker and _search_regex_in_list(test_output.split('\n'), _GN_GEN_REGEX)): 177*6777b538SAndroid Build Coastguard Worker assert not ran_gn_gen 178*6777b538SAndroid Build Coastguard Worker ran_gn_gen = True 179*6777b538SAndroid Build Coastguard Worker test_output = _do_build_get_failure_output(gn_path, 'gen', options) 180*6777b538SAndroid Build Coastguard Worker 181*6777b538SAndroid Build Coastguard Worker if (not _find_regex_in_test_failure_output(test_output, expect_regex) 182*6777b538SAndroid Build Coastguard Worker and not did_clean_build): 183*6777b538SAndroid Build Coastguard Worker # Ensure the failure is not due to incremental build. 184*6777b538SAndroid Build Coastguard Worker did_clean_build = True 185*6777b538SAndroid Build Coastguard Worker test_output = _do_build_get_failure_output(gn_path, 'clean', options) 186*6777b538SAndroid Build Coastguard Worker 187*6777b538SAndroid Build Coastguard Worker if not _find_regex_in_test_failure_output(test_output, expect_regex): 188*6777b538SAndroid Build Coastguard Worker if test_output is None: 189*6777b538SAndroid Build Coastguard Worker # Purpose of quotes at beginning of message is to make it clear that 190*6777b538SAndroid Build Coastguard Worker # "Compile successful." is not a compiler log message. 191*6777b538SAndroid Build Coastguard Worker test_output = '""\nCompile successful.' 192*6777b538SAndroid Build Coastguard Worker error_message = '//{} failed.\nExpected compile output pattern:\n'\ 193*6777b538SAndroid Build Coastguard Worker '{}\nActual compile output:\n{}'.format( 194*6777b538SAndroid Build Coastguard Worker gn_path, expect_regex, test_output) 195*6777b538SAndroid Build Coastguard Worker error_messages.append(error_message) 196*6777b538SAndroid Build Coastguard Worker 197*6777b538SAndroid Build Coastguard Worker if error_messages: 198*6777b538SAndroid Build Coastguard Worker raise Exception('\n'.join(error_messages)) 199*6777b538SAndroid Build Coastguard Worker 200*6777b538SAndroid Build Coastguard Worker if options.stamp: 201*6777b538SAndroid Build Coastguard Worker build_utils.Touch(options.stamp) 202*6777b538SAndroid Build Coastguard Worker 203*6777b538SAndroid Build Coastguard Worker 204*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__': 205*6777b538SAndroid Build Coastguard Worker main() 206