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