xref: /aosp_15_r20/external/cronet/build/android/gyp/nocompile_test.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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